{"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":"2c64e994-a744-4249-b94f-66345c16b289","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"3cc1b5b0f2cd15b77c20498ef0c2ed200c68afa2230677173b2959865cbe5f9b","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":"836af2e69773a3d7d8b62f85cb2b9301f27514a846501e3393ce9cc3937bb591","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":"90c19e8c2eef25f13b827159bf18a768daab66247b9d7f80c95a691932ba5108","dependsOn":["build_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","state":"accepted"},{"acceptedSubmissionHash":"22b7482b84842927bf7e6c87727aaca247f8b377742373adee523902d05bc48b","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":"f272fc00522dd5615a13c3c1b980c758a75e348a9a3c2f9e936a4a083fe9f542","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":"4a50f6ff9e5b2a469a6e9b9e62980b09fac2fe5422a59e5fb2fa5de004ef63ae","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"d59561da3b378461b7c749f6bd5084477d44993aa94972ddea90f542535751df","skillId":"build-contract-project","tools":[]},"key":"build_contract_project","kind":"code","role":"implement","skillHash":"d59561da3b378461b7c749f6bd5084477d44993aa94972ddea90f542535751df","skillId":"build-contract-project","state":"accepted"},{"acceptedSubmissionHash":"61c5ea96f51bd3b956db6d3bdf6f0da7646b12e021fb9355548f27fbedb2bd3b","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"44e3a8f49ffa5a7d1a50bc32994423dbc6e25ee8df193e44dd85960e07626a36","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"4c76766b00591dfe982fe85d3de589fdb2f810e5137c301155d1c5e36a1d3b23","skillId":"write-foundry-tests","tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"4c76766b00591dfe982fe85d3de589fdb2f810e5137c301155d1c5e36a1d3b23","skillId":"write-foundry-tests","state":"accepted"}],"objective":"Build and deploy BetPool: a live pari-mutuel betting pool for NIGHTSHIFT's games (NIGHT CHESS, Space Pinball) and Agent Arena. Spectators bet on side A or B while a match is played. Each bet carries a weight the NIGHTSHIFT server signs into a short-lived ticket (early and underdog bets weigh more, late bets on a clear leader less). Winners get their bet back plus a share of the losing side by weighted stake, minus a 5% fee taken only from the losing side. Results come from NIGHTSHIFT's result signer, as in its GameEscrow 0xf460fd9ad76f18414f54a2431870e8a116193489.\n\nTokens: only IMD 0xd34a99bc0f67ae1bbd63c660e6d0b0dd03e263b7 and SHIFT 0xe5e58e53d0bb204aa62c995a6c8cc8a48e60c3ac (plain ERC-20, 18 decimals, no transfer fee). One token per market. Per-bet limits set in the constructor: IMD 0.1 to 500, SHIFT 1000 to 25000000 (whole tokens). Owner may setLimits(token,min,max) (event LimitsSet), never above hard caps fixed at deploy (10000 IMD, 500000000 SHIFT), min > 0, min <= max.\n\nSignatures: EIP-712 by the result signer, domain \"NightshiftBets\", version \"1\", chainId, this contract.\n\n1. createMarket(bytes32 marketId, address token, uint64 closesAt, bytes32 game, bytes sig): signed Market(bytes32 marketId,address token,uint64 closesAt,bytes32 game); anyone submits; marketId new; closesAt in the future, at most 7 days ahead. Event MarketCreated(marketId, token, closesAt, game).\n2. bet(bytes32 marketId, uint8 side, uint256 amount, uint16 weightBps, uint64 expiry, bytes32 nonce, bytes sig): side 0=A, 1=B; signed Ticket(bytes32 marketId,address bettor,uint8 side,uint256 amount,uint16 weightBps,uint64 expiry,bytes32 nonce) with bettor = msg.sender, not expired, expiry at most 120 s after block time, nonce unused, weightBps 500..10000, market exists, not settled, before closesAt, amount within limits (exact transferFrom). Records amount and shares = amount*weightBps/10000 per wallet and side; a wallet may bet many times and on both sides. Event BetPlaced(marketId, bettor, side, amount, weightBps, shares).\n3. settle(bytes32 marketId, uint8 outcome, uint256 deadline, bytes sig): anyone submits signed Outcome(bytes32 marketId,uint8 outcome,uint256 deadline); 0=A won, 1=B won, 2=void; existing unsettled market, unexpired signature. Settling closes betting. Void, or no bets on the winning or losing side: refundable, everyone gets exactly their bets back, no fee. Otherwise fee = 5% of the losing side's total (rounded down), sent at settlement to the fee splitter 0x1119d5df2f046bc2d3c0f6279ef2e9ce49422d44; the other 95% is shared by winners pro rata to winning-side shares. Event Settled(marketId, outcome, fee).\n4. claim(bytes32 marketId) and claimMany(bytes32[]): pull payment; winners get their winning-side amounts plus their share; in a refundable market everyone gets their amounts back; once per wallet; losers get nothing; dust at most 1 wei per winner stays; no claim pays more than the market holds. Event Claimed(marketId, bettor, amount).\n5. expire(bytes32 marketId): if unsettled 24 h after closesAt, anyone makes it refundable, no fee. Event Expired(marketId).\n6. Views: markets(id) -> token, closesAt, game, totalA, totalB, sharesA, sharesB, settled, outcome, refundable; betsOf(id, wallet) -> amountA, amountB, sharesA, sharesB, claimed; claimable(id, wallet); limits(token); usedNonce(bytes32); signer(); paused().\n\nOwner 0x6c4c78f2c3bdf4c4318fe305fb1c8bc1694d19b5 can only: setSigner (event SignerSet), setLimits within the caps, pause/unpause new markets and bets (settle, expire, claim always work). Never move or redirect bets, change the fee, recipient or tokens, or settle without the signer. No upgradeability, delegatecall, selfdestruct or token sweeping. Reentrancy guard plus checks-effects-interactions, SafeERC20, Solidity 0.8.x, NatSpec, events on every state change.\n\nFoundry tests: full flows for both tokens, many bettors on both sides at different weights and amounts at and between limits; payouts sum to the pot minus fee and follow weighted shares; fee exactly 5% of the losing side to the splitter with odd amounts; void and one-sided markets refund all, no fee; expiry; tickets refused for wrong bettor, side, amount, weight out of range, expired, too-far expiry, reused nonce, wrong signer, contract or chain; bets after closesAt, on settled or unknown markets, outside limits refused; signature checks on create and settle; no double settle or claim; losers get nothing; pause; owner cannot move funds; fuzz and invariants: the contract always holds what it owes and payouts never exceed bets.","parentJobId":null,"planHash":"03289a1ed9523a98e7b6a43c58f6db93a1b0008e98953848dae1ef56f8ff58b8","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"2c64e994-a744-4249-b94f-66345c16b289","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-2229-build-deploy-betpool-live"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"52479","feedbackHash":"e12118d62d1e5eb17975f68d2350e52dcb96a90ce5b4d9ca199f1932fd71af11","nodeKey":"audit_economics","submissionHash":"3cc1b5b0f2cd15b77c20498ef0c2ed200c68afa2230677173b2959865cbe5f9b","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52017","feedbackHash":"d4f42b1798aac19f1e764a152f92f045ae6541a0698c0af488885d3f05657c57","nodeKey":"audit_flow","submissionHash":"836af2e69773a3d7d8b62f85cb2b9301f27514a846501e3393ce9cc3937bb591","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51557","feedbackHash":"8d54feec705aa5aed8766c8a4dd70d168b049c95bdede572a23682d616a69ab4","nodeKey":"audit_judge","submissionHash":"a0358574ba50e5b86b9f5089cf3682420f8fb80809266fd49aae7d17bed1bfa6","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52641","feedbackHash":"7af6ecbb49ee4bcdd66df4a4aeedbd5a0387b0851362dce106d4e7a2e6ba1381","nodeKey":"audit_judge","submissionHash":"90c19e8c2eef25f13b827159bf18a768daab66247b9d7f80c95a691932ba5108","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51128","feedbackHash":"a7074244604550295c93dfd47e0f96e4c63438c87d870e8aca0d0eef1181103d","nodeKey":"audit_math","submissionHash":"22b7482b84842927bf7e6c87727aaca247f8b377742373adee523902d05bc48b","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51140","feedbackHash":"773a8c02357e53faa64c87e8d144fdf2a0ef92ca3045b12b71d6f9c9e628b535","nodeKey":"audit_permissions","submissionHash":"f272fc00522dd5615a13c3c1b980c758a75e348a9a3c2f9e936a4a083fe9f542","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"53923","feedbackHash":"336caefa1a8b4d974e602f0b2569c216e7f92b7f4863ef3ec1ff75a314f7be11","nodeKey":"build_contract_project","submissionHash":"4a50f6ff9e5b2a469a6e9b9e62980b09fac2fe5422a59e5fb2fa5de004ef63ae","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52469","feedbackHash":"5716257d7d51320a82c7d1e17c27d52462f75c74522bf41eccc555f47b69895a","nodeKey":"build_contract_project","submissionHash":"b64ca97f5339a2b3a41c711bb729d5e0ab0a74fb62805bd3871cfa9525be6940","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51474","feedbackHash":"5924a40a6eac64f2030025545ce64c35dc28ba98398ecd444a6774d89183e4bc","nodeKey":"manifest","submissionHash":"61c5ea96f51bd3b956db6d3bdf6f0da7646b12e021fb9355548f27fbedb2bd3b","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52614","feedbackHash":"b2b9fe005d330bc8f159aaeb6aca163dc18433a11ab7b82ff1a403219fae38d3","nodeKey":"manifest","submissionHash":"38f091b913db03b03410e15b57af0030ba81d939abe12c6975065d7f15ab524e","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51130","feedbackHash":"6732d21bde16e2804a7cbe4613f1ed338217e55cd3a3aecc7d6c9f56608ee878","nodeKey":"write_foundry_tests","submissionHash":"e237b435bfe486482a14ed9307497e0b09134550fd9501abf0462285660c7484","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51742","feedbackHash":"e489bce597742b22d449fae007503a5cd6065f57165905374d0d8fa06d011619","nodeKey":"write_foundry_tests","submissionHash":"44e3a8f49ffa5a7d1a50bc32994423dbc6e25ee8df193e44dd85960e07626a36","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"82f5a9d5470bf941d54f318462ccd9cc04a55f2f272cd7dda4fe9034892c45f7","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"48cf2e19b6c54386","findings":[{"citation":"resolved","description":"Betting is only closed by block.timestamp >= closesAt or by settled == true. settle() has no relation to closesAt, so during the window between the match ending and the settle transaction being mined, any wallet that still holds an unexpired ticket (up to 120 s old, minimum weight 500 bps) can front-run settle() with bet() on the side named in the pending Outcome. The bet is accepted (market open, ticket valid), then the market settles and that wallet receives its full principal plus losing-side share. Its downside is zero because it already knows the result. The contract does not enforce that bets end before results can arrive; it relies on the NIGHTSHIFT server never issuing tickets near the end of a match. This is a trust assumption on the ticket signer rather than an arithmetic defect, and the brief explicitly permits settlement before closesAt, so it is reported as low. A contract-side mitigation that preserves the design is to have the Outcome also carry (or the market store) a bettingCutoff that settle() enforces as settledAt >= cutoff, or to refuse bets whose ticket was issued (expiry - 120) after a signed 'results pending' timestamp.","line":232,"path":"src/BetPool.sol","reproduction":"State: market ID on IMD with closesAt = now+1h; A: 1 IMD @10000, B: 1 IMD @10000. Attacker holds ticket (side 0, amount 500 IMD, weightBps 500, expiry = issue+120) signed 100 s ago. Sequence: (1) attacker observes settle(ID, 0, deadline, sig) in the mempool, (2) attacker calls bet(ID,0,500e18,500,expiry,nonce,sig) in the same block ahead of it: accepted (block.timestamp < closesAt, expiry >= now), (3) settle executes: fee = 1e18/20 = 0.05e18, winning shares = 1e18 + 25e18, (4) attacker claim() returns 500e18 + mulDiv(0.95e18, 25e18, 26e18) = 500913461538461538461. Expected: no stake can be placed once the result is known; actual: attacker turns 500 IMD into 500.913 IMD with no possibility of loss. Reproduced in test/scratch/Probe.t.sol::test_frontRunSettle.","severity":"low","snippet":"    function settle(bytes32 marketId, uint8 outcome, uint256 deadline, bytes calldata sig) external nonReentrant {","title":"settle() is accepted before closesAt, so a bettor holding a live 120 s ticket can bet on the known winner after seeing the signed Outcome and collect a risk-free share of the losing pot"},{"citation":"resolved","description":"shares = amount * weightBps / 10000 floors to zero whenever amount * weightBps < 10000. The brief says a ticket is accepted when 'amount within limits' and 'weightBps 500..10000'; setLimits permits min = 1 wei, so under such limits a validly signed, in-limit ticket is rejected. The revert is the safe choice (accepting a zero-share winner would leave a nonempty winning side with zero shares and no defined pro-rata payout), and with the deployed minima (0.1 IMD, 1000 SHIFT) the branch is unreachable, so this is informational: the precondition is a deliberate owner action and the only effect is a refused bet. The NatSpec already documents it; the brief's acceptance rule could be clarified to 'min >= 20 base units' or setLimits could enforce min >= 20 so the two rules cannot diverge.","line":217,"path":"src/BetPool.sol","reproduction":"Owner calls setLimits(IMD, 1, 500e18) (accepted: min > 0, min <= max, max <= cap). Market ID on IMD open. Bettor presents a signer-valid ticket amount = 19, weightBps = 500 (19*500/10000 = 0): bet() reverts ZeroShares although 1 <= 19 <= 500e18 and 500 is in range. Same for amount = 1, weightBps = 9999. amount = 20, weightBps = 5000 is accepted with 10 shares. Reproduced in test/scratch/Probe.t.sol::test_zeroSharesRefusal.","severity":"info","snippet":"        if (shares == 0) revert ZeroShares();","title":"Tickets within owner-set limits and weight range are refused with ZeroShares when amount*weightBps < 10000 (only after the owner lowers a minimum below 20 wei)"}],"hash":"22b7482b84842927bf7e6c87727aaca247f8b377742373adee523902d05bc48b","nodeId":"07823651-2306-491e-a91e-022cf00a5f20","outcome":"completed","summary":"The review is complete. The findings file is at the repository root with two findings and a coverage row for all ten entry points plus three invariants.\n\n**Outcome:** the arithmetic in BetPool holds. I found no precision, rounding, overflow, or boundary defect that loses or misdirects funds.\n\n**What I verified in the math area**\n\n- Shares are floored per ticket and summed per wallet and side. The hard caps bound the multiplication at 5e30, far below overflow.\n- The fee is an exact floor of 5% of the losing side. The same expression is used by settlement and by the per-winner payout, so the two never drift.\n- Each winner's bonus rounds down once via mulDiv, so claims plus fee never exceed the pot. A probe with seven unequal winners and odd losing totals left 3 wei of dust. Refundable markets pay out to exactly zero.\n- The divisor can never be zero because every accepted bet carries at least one share.\n- Every time edge matches the brief exactly: closesAt is exclusive, 7 days and 120 seconds are inclusive maxima, expiry at closesAt plus 24 hours is inclusive.\n- The existing suite of 85 tests, including fuzz and invariant runs, passes.\n\n**Findings reported**\n\n1. **Low.** Settlement is accepted before closesAt. A wallet holding a still-valid ticket can see the signed Outcome in the mempool and bet on the known winner ahead of it. In my probe, 500 IMD at minimum weight returned 500.91 IMD with no possible loss. This is a trust assumption on the ticket signer rather than a code bug, and the brief allows early settlement, so I rated it low.\n2. **Info.** If the owner lowers a minimum below 20 wei, a validly signed in-limit ticket whose amount times weight is under 10000 reverts with ZeroShares. Unreachable with the deployed limits and the revert is the safe choice.\n\n**Not reached:** nothing in the assigned area. Access control, signature replay across chains, and the launch manifest belong to other reviewers and I did not trace them beyond what the coverage rows state.\n\nThe scratch probes live under test/scratch/Probe.t.sol and are not part of the submission.","treeHash":null,"usage":{"cachedInputTokens":936574,"inputTokens":418,"model":"claude-fable-5-1","outputTokens":23203,"runtime":"claude","turns":22,"wallClockMs":437133}},{"artifacts":[],"attempt":1,"bundleHash":"ecac7cac34837783e88e4adc490378a3b0cbe8d10f8f1ecd643e199a2cd55886","device":"9506ecec995bdbf9","findings":[],"hash":"38f091b913db03b03410e15b57af0030ba81d939abe12c6975065d7f15ab524e","nodeId":"f659562b-ac1c-4a9d-8600-2e593a5caf3e","outcome":"completed","summary":"I wrote `launch.json`, and it is the only file I changed. The build passes and all 85 tests pass when run with the required fuzz seed. I couldn't commit it, because git has no author name or email set on this machine and I didn't want to change the git config. So the file is staged but not committed.\n\nThe manifest deploys one contract, `BetPool`, with no constructor arguments (`\"constructorArgs\": []`). That's because the accepted source takes no arguments: the owner, both tokens, the fee splitter, the 5% fee and the hard caps are all fixed in the code. The starting bet limits are also set in the code (IMD 0.1–500, SHIFT 1,000–25,000,000). Since the brief names the owner address directly, it is fixed in the contract instead of using `$owner`. There are no other contracts, since the helpers and mock tokens in `test/` are not deployed.\n\nThe brief doesn't give the NIGHTSHIFT result signer's address, so the contract starts with no signer set. Until the owner calls `setSigner` with NIGHTSHIFT's real signing address (the one their GameEscrow uses), creating markets, betting and settling will all fail. The notes in the manifest say this, and also list the owner's limited powers and the fixed addresses.\n\nThe file has only the three allowed fields (`kind`, `contracts`, `notes`), and the notes are 1,037 characters, well under the 4,000 limit.","treeHash":"83fa49c600838ad16fbdf1ac88cb995f672b32a4","usage":{"cachedInputTokens":183955,"inputTokens":12,"model":"claude-opus-5-5","outputTokens":2578,"runtime":"claude","turns":9,"wallClockMs":111621}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"dfa08759ca0424de","findings":[{"citation":"resolved","description":"Flow gap (execution x first principles). The brief intends a bet weight to reflect the match state when the bet is made (early/underdog bets weigh more, late bets on a clear leader less) and settlement to close betting. On-chain, bet() accepts any ticket whose expiry is between block.timestamp and block.timestamp + 120 s (line 205), and settle() can be relayed at any time, including before closesAt and in the same block as pending bets. Nothing separates the last accepted bet from the outcome: there is no close step, no minimum gap, and bets carry no timestamp the Outcome could exclude. A spectator therefore requests a ticket for side A and one for side B while the result is open (betting both sides is allowed), holds both, and submits only the winning one once the result is known, either from watching the game in the last two minutes or from seeing the Outcome relay in the mempool, with a higher priority fee so the bet is ordered first. The bet carries zero risk and is paid a weighted share of the losing pool, diluting every honest winner. The README asks the operator to stop issuing tickets once the result is known, but the ticket is valid for up to 120 s after issuance, so operator discipline cannot remove the window; only the contract can. Concrete economics from the reproduction: honest A 100 IMD, honest B 100 IMD, both weight 10000. Attacker holds 500 IMD tickets for both sides at weight 10000, submits the side-A ticket 90 s later in the block that settles A. Fee 5 IMD. Attacker receives 500 + 95*5000/6000 = 579.1667 IMD (profit 79.17 IMD, zero risk). Honest A bonus falls from 95 IMD to 15.83 IMD. Under the brief's intent the attacker's late bet should earn no bonus. Fix needs a scope decision because the brief says settling closes betting in one step: either (a) require block.timestamp >= closesAt in settle() so the signer sets closesAt to the expected match end and bets stop before any outcome can land, or (b) add a signed close step (or a closesAt override inside the Outcome message applied in an earlier block) so that no bet can be ordered in the same block as the outcome, and shorten MAX_TICKET_LIFETIME if the operator cannot bound result latency below 120 s. The attached proof encodes the intended guarantee (a bet landing in the settlement block earns no bonus); it fails on this code and is expected to pass once settlement is separated from pending bets.","line":232,"path":"src/BetPool.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {BetPool} from \"src/BetPool.sol\";\n\ncontract ScratchToken is ERC20 {\n    constructor() ERC20(\"T\", \"T\") {}\n    function mint(address to, uint256 amount) external { _mint(to, amount); }\n}\n\n/// @notice Shows that a ticket issued while the result was unknown can be submitted in the same\n///         block as, and ahead of, the signed Outcome that settles the market, and that the\n///         holder is paid a share of the losing pool for a bet that carried no risk.\ncontract SettleRaceTest is Test {\n    address constant OWNER = 0x6C4c78f2C3bDf4C4318Fe305FB1C8BC1694d19b5;\n    address constant IMD = 0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7;\n    uint256 constant SIGNER_KEY = 0xA11CE;\n    bytes32 constant ID = keccak256(\"night chess 7\");\n    bytes32 constant DOMAIN_TYPEHASH =\n        keccak256(\"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\");\n\n    BetPool pool;\n    address honestA = makeAddr(\"honestA\");\n    address honestB = makeAddr(\"honestB\");\n    address attacker = makeAddr(\"attacker\");\n\n    function setUp() public {\n        vm.warp(1_800_000_000);\n        vm.chainId(1);\n        pool = new BetPool();\n        vm.prank(OWNER);\n        pool.setSigner(vm.addr(SIGNER_KEY));\n        vm.etch(IMD, address(new ScratchToken()).code);\n    }\n\n    function _sign(bytes32 structHash) internal view returns (bytes memory) {\n        bytes32 domain = keccak256(\n            abi.encode(DOMAIN_TYPEHASH, keccak256(\"NightshiftBets\"), keccak256(\"1\"), uint256(1), address(pool))\n        );\n        bytes32 digest = keccak256(abi.encodePacked(\"\\x19\\x01\", domain, structHash));\n        (uint8 v, bytes32 r, bytes32 s) = vm.sign(SIGNER_KEY, digest);\n        return abi.encodePacked(r, s, v);\n    }\n\n    function _ticket(address bettor, uint8 side, uint256 amount, uint16 w, uint64 expiry, bytes32 nonce)\n        internal\n        view\n        returns (bytes memory)\n    {\n        return _sign(\n            keccak256(abi.encode(pool.TICKET_TYPEHASH(), ID, bettor, side, amount, w, expiry, nonce))\n        );\n    }\n\n    function _bet(address bettor, uint8 side, uint256 amount, uint16 w, uint64 expiry, bytes32 nonce) internal {\n        ScratchToken(IMD).mint(bettor, amount);\n        vm.startPrank(bettor);\n        IERC20(IMD).approve(address(pool), amount);\n        pool.bet(ID, side, amount, w, expiry, nonce, _ticket(bettor, side, amount, w, expiry, nonce));\n        vm.stopPrank();\n    }\n\n    function test_heldTicketFrontRunsOutcomeAndIsPaidFromLosers() public {\n        uint64 closesAt = uint64(block.timestamp + 1 hours);\n        pool.createMarket(ID, IMD, closesAt, bytes32(\"chess\"), _sign(\n            keccak256(abi.encode(pool.MARKET_TYPEHASH(), ID, IMD, closesAt, bytes32(\"chess\")))\n        ));\n        // Honest bettors at t0: 100 IMD on A, 100 IMD on B, full weight.\n        _bet(honestA, 0, 100 ether, 10_000, uint64(block.timestamp + 120), bytes32(\"hA\"));\n        _bet(honestB, 1, 100 ether, 10_000, uint64(block.timestamp + 120), bytes32(\"hB\"));\n\n        // t0: the match is still open. The attacker asks the server for one ticket per side\n        // (betting both sides is allowed by the brief) and holds both without submitting.\n        uint64 expiry = uint64(block.timestamp + 120);\n        bytes memory ticketA = _ticket(attacker, 0, 500 ether, 10_000, expiry, bytes32(\"atkA\"));\n        bytes memory ticketB = _ticket(attacker, 1, 500 ether, 10_000, expiry, bytes32(\"atkB\"));\n\n        // t0 + 90 s: the game ends, A wins, the signer broadcasts settle(ID, 0, ...).\n        vm.warp(block.timestamp + 90);\n        uint256 deadline = block.timestamp + 1 hours;\n        bytes memory outcomeSig = _sign(keccak256(abi.encode(pool.OUTCOME_TYPEHASH(), ID, uint8(0), deadline)));\n\n        // Same block, higher priority fee: the attacker submits only the winning ticket.\n        ScratchToken(IMD).mint(attacker, 500 ether);\n        vm.startPrank(attacker);\n        IERC20(IMD).approve(address(pool), 500 ether);\n        pool.bet(ID, 0, 500 ether, 10_000, expiry, bytes32(\"atkA\"), ticketA);\n        vm.stopPrank();\n        ticketB; // never submitted\n\n        pool.settle(ID, 0, deadline, outcomeSig);\n\n        uint256 attackerPayout = pool.claimable(ID, attacker);\n        uint256 honestPayout = pool.claimable(ID, honestA);\n        emit log_named_decimal_uint(\"attacker principal\", 500 ether, 18);\n        emit log_named_decimal_uint(\"attacker payout   \", attackerPayout, 18);\n        emit log_named_decimal_uint(\"honestA payout    \", honestPayout, 18);\n        // Expected under the brief's intent: a bet placed once the result is known carries no\n        // claim on the losing pool. Actual: 500 + 95 * 500/600 = 579.166.. IMD; honestA's bonus\n        // falls from 95 to 15.83 IMD.\n        assertLe(attackerPayout, 500 ether, \"risk-free bet landed ahead of settlement and took the losers' pool\");\n    }\n\n    function test_expireFrontRunsLateSettlement() public {\n        uint64 closesAt = uint64(block.timestamp + 1 hours);\n        pool.createMarket(ID, IMD, closesAt, bytes32(\"chess\"), _sign(\n            keccak256(abi.encode(pool.MARKET_TYPEHASH(), ID, IMD, closesAt, bytes32(\"chess\")))\n        ));\n        _bet(honestA, 0, 100 ether, 10_000, uint64(block.timestamp + 120), bytes32(\"hA\"));\n        _bet(honestB, 1, 100 ether, 10_000, uint64(block.timestamp + 120), bytes32(\"hB\"));\n\n        // The signer relays the outcome only at closesAt + 24h. honestB (the loser) sees it and\n        // front-runs with expire(): the market becomes refundable, honestB loses nothing.\n        vm.warp(uint256(closesAt) + 24 hours);\n        uint256 deadline = block.timestamp + 1 hours;\n        bytes memory outcomeSig = _sign(keccak256(abi.encode(pool.OUTCOME_TYPEHASH(), ID, uint8(0), deadline)));\n        vm.prank(honestB);\n        pool.expire(ID);\n        vm.expectRevert(BetPool.MarketAlreadySettled.selector);\n        pool.settle(ID, 0, deadline, outcomeSig);\n        assertEq(pool.claimable(ID, honestB), 100 ether);\n        assertEq(pool.claimable(ID, honestA), 100 ether);\n    }\n}","reproduction":"State: signer configured, market ID on IMD with closesAt = now + 1 h. t0: honestA bet(ID,0,100e18,10000,t0+120,nA), honestB bet(ID,1,100e18,10000,t0+120,nB). Attacker obtains signed tickets Ticket(ID,attacker,0,500e18,10000,t0+120,atkA) and Ticket(ID,attacker,1,500e18,10000,t0+120,atkB) and submits neither. t0+90: signer broadcasts settle(ID,0,deadline,sig). Attacker front-runs in the same block with bet(ID,0,500e18,10000,t0+120,atkA,sigA); both succeed. Expected: a bet placed after the result is known earns no share of the losers. Actual: claimable(ID,attacker) = 579166666666666666666 (principal 500e18 + 79.17e18 bonus); claimable(ID,honestA) drops from 195e18 to 115833333333333333333. Run: forge test --match-path test/scratch/SettleRace.t.sol --match-test test_heldTicketFrontRunsOutcomeAndIsPaidFromLosers -vv (fails on this code with 579166666666666666666 > 500000000000000000000).","severity":"medium","snippet":"    function settle(bytes32 marketId, uint8 outcome, uint256 deadline, bytes calldata sig) external nonReentrant {","title":"Held tickets give a free option: a bet can land in the same block as, and ahead of, the signed Outcome and take a share of the losing pool"},{"citation":"resolved","description":"Flow gap between settle() and expire(). Once block.timestamp >= closesAt + 24 h both finalization paths are open at once and the first to land wins (README line 72 documents the race). expire() is permissionless and needs no signature, so any bettor on the side that lost can watch the mempool for the signer's settle() and send expire() with a higher priority fee: the market becomes refundable, the signed outcome is rejected with MarketAlreadySettled forever, winners lose their bonus and the splitter loses its fee. This follows the brief's wording for expire(), so it is reported as a design-level race for a scope decision rather than a code bug: the signer must relay every outcome well inside 24 h of closesAt, or settle() should take precedence for a grace period (for example, settle() allowed and expire() refused until closesAt + 24 h, then expire() allowed only if no valid outcome has been relayed within a further short window), or the Outcome signature should be accepted after expiry to convert a refund into a result before any claim is paid.","line":258,"path":"src/BetPool.sol","reproduction":"State: market ID (IMD, closesAt = T), honestA bet 100e18 on A, honestB bet 100e18 on B, both weight 10000, A wins. At block.timestamp = T + 24 h the signer broadcasts settle(ID,0,deadline,sig). honestB sends expire(ID) in the same block ahead of it. Expected per the outcome: honestA claims 195e18, honestB 0, splitter receives 5e18. Actual: expire succeeds, settle reverts with MarketAlreadySettled, claimable(ID,honestA) = 100e18 and claimable(ID,honestB) = 100e18, no fee. Run: forge test --match-path test/scratch/SettleRace.t.sol --match-test test_expireFrontRunsLateSettlement (the test passes, confirming the sequence executes).","severity":"low","snippet":"        if (block.timestamp < uint256(market.closesAt) + REFUND_DELAY) revert TooEarlyToExpire();","title":"After closesAt + 24 h a losing bettor can front-run a late settle() with expire() and void the market"}],"hash":"3cc1b5b0f2cd15b77c20498ef0c2ed200c68afa2230677173b2959865cbe5f9b","nodeId":"4a88c760-99fd-4974-9095-d79be22461ae","outcome":"completed","summary":"The review is complete and the findings file is written and parsed back as valid JSON.\n\n**Outcome.** The economic core of `src/BetPool.sol` holds: every bet adds exactly the received amount, the fee is floor(5%) of the losing side taken only on two-sided non-void markets, each winner's bonus floors once so payouts never exceed the pot minus fee, refunds return exact stakes, and the per-market ledger blocks any claim beyond what the market holds. All 85 supplied tests pass, including the invariant campaign.\n\n**Findings recorded in `.imd-findings.json`:**\n\n- **Medium, held-ticket free option.** A ticket is valid up to 120 s after issuance, and settle() can land in the same block as pending bets with no close step between them. A spectator holds tickets for both sides, then submits only the winning one ahead of the Outcome relay. Reproduced: the attacker turns a risk-free 500 IMD bet into 579.17 IMD and cuts an honest winner's bonus from 95 to 15.83 IMD. The scratch test in `test/scratch/SettleRace.t.sol` fails on this code and is attached as proof. The fix needs a scope decision since the brief specifies one-step settlement.\n- **Low, expire() front-runs a late settle().** At closesAt + 24 h both paths are open and a losing bettor can void the market ahead of the signed outcome. This follows the brief's wording, so it is flagged as a design race rather than a code bug.\n\n**Coverage.** All ten entry points have rows: bet, settle and expire map to the findings, the other seven hold. Three invariant rows (balance covers liabilities, payouts bounded by pot minus fee, fee rule) also hold. The owner-appoints-signer trust assumption is documented in the README and recorded as by-design, not as a finding.\n\n**Not reached.** I did not verify on-chain that the two token addresses are plain ERC-20s, and I did not examine the launch manifest since none exists in the tree yet.","treeHash":null,"usage":{"cachedInputTokens":983401,"inputTokens":450,"model":"claude-fable-5-1","outputTokens":25711,"runtime":"claude","turns":15,"wallClockMs":553035}},{"artifacts":[],"attempt":1,"bundleHash":"455b893dbfbb26e416966390a2526bf285219f236f5a0e0fcd96dcee59705e7d","device":"ce319efac2b76da0","findings":[{"description":"The revised settle() rejects outcome 0/1 while block.timestamp < closesAt (BettingStillOpen) and rejects every outcome from closesAt + 24 hours (SettlementWindowClosed). The brief says 'Settling closes betting', which suggests a result could be relayed before closesAt. The new rule closes the held-ticket race (a spectator submitting only the winning ticket in the Outcome's block), and the README documents it. The cost: a match that ends early pays out only once closesAt is reached, which can be up to 7 days away. If NIGHTSHIFT misses the 24 h window, a correct result can no longer be applied and the market refunds. The tests treat this as the intended design (BetPoolSafety window tests; BetPoolAttackHandler.settleOutsideWindow / betAfterClose). The requester should confirm the trade-off is acceptable.","line":249,"path":"src/BetPool.sol","reproduction":"createMarket closesAt = now + 1h; place bets on both sides; settle(id, 0, now + 1h, validSig) at now -> reverts BettingStillOpen (the brief could be read as expecting success). warp(closesAt + 24h); settle(id, 0, deadline, validSig) -> reverts SettlementWindowClosed; only expire() is possible.","severity":"info","title":"Decisive settlement is refused before closesAt and all settlement from closesAt + 24 h (stricter than the brief)"}],"hash":"44e3a8f49ffa5a7d1a50bc32994423dbc6e25ee8df193e44dd85960e07626a36","nodeId":"ae38615b-14b6-4469-8055-ccf2db4a660e","outcome":"completed","summary":"My tests pass again on the revised BetPool: `forge build` succeeds, and `forge test` with the verifier's seed passes all 106 tests (8 suites, 0 failed).\n\n**Why my tests broke.** The implementation now restricts when `settle` works. A winning result (A or B) is only accepted from `closesAt` onward, and no result at all is accepted from `closesAt + 24h`. That broke 3 tests in `test/BetPoolOracle.t.sol` and both invariants in the attack suite. I changed the tests in only three files, and every assertion is still as strict as before:\n- **`testFuzz_randomKeyNeverAuthorizes`**: the settle attempt now runs at `closesAt`. Otherwise it is refused for being early, and the test wouldn't show that a random key's signature is rejected.\n- **`test_settleWithSignatureForOtherMarketOrOutcomeRefused`**: moves the clock to `closesAt` first, for the same reason.\n- **`test_pauseBlocksCreateAndBetOnly`**: settles the market while paused before moving the clock past the 24h window. It still checks that settle, expire and claim all work during a pause.\n- **Attack handler**: its honest `settle` now stays inside the new window. I also added two attacks to the random sequences. One submits a validly signed result outside the window; the other places a validly signed bet at or after `closesAt` or on a settled market. Both must revert without moving funds or using up the ticket's nonce, and they did, with the two invariants passing over 64 runs.\n\n**Signer check.** The implementation now starts with a hardcoded signer, `0x3B5919586FC04F7e99b450a72bcB14Eeb6977497`. I called GameEscrow's `signer()` on Ethereum mainnet through a public RPC node, and it returns that same address. An existing test already checks that a new deployment starts with it.\n\n**Finding.** I found no defect. I recorded one info-level item in `.imd-findings.json` so the requester can confirm the trade-off. The brief says \"settling closes betting\", which suggests a result could be posted before `closesAt`; the new rule refuses that. The rule fixes the earlier exploit where a spectator held tickets for both sides and submitted only the winning one in the same block as the result. The cost:\n- A match that ends early pays out only once `closesAt` arrives, which can be up to 7 days away.\n- If NIGHTSHIFT doesn't post the result within 24 hours of `closesAt`, the correct result can no longer be applied and the market can only be refunded.\n\nI haven't committed the changes.","treeHash":"fdea24f0b3dbde153e858d64a62925a1ea543cbd","usage":{"cachedInputTokens":792411,"inputTokens":26,"model":"claude-opus-5-5","outputTokens":9521,"runtime":"claude","turns":15,"wallClockMs":414928}},{"artifacts":[],"attempt":1,"bundleHash":"1a9f8761bdf56139d0d66278aa73edc7ec279a08743e3f3c53a0d04e8db8cac8","device":"733f63ff9321d57a","findings":[],"hash":"4a50f6ff9e5b2a469a6e9b9e62980b09fac2fe5422a59e5fb2fa5de004ef63ae","nodeId":"2c67bb99-aa28-4482-b488-22cfc6182819","outcome":"completed","summary":"The revision is complete. All checks pass and every finding is answered in `.imd-responses.json`.\n\n**What I reproduced and fixed**\n\n- **Held-ticket race (medium, with proof).** The proof failed on the starting tree exactly as reported, paying the risk-free bettor 579 IMD on a 500 IMD stake. The fix is the reviewer's option (a): `settle` now refuses a decisive outcome before `closesAt` with `BettingStillOpen`. Bets need a timestamp strictly below `closesAt` and decisive outcomes need one at or above it, so a bet and the Outcome deciding it can never share a block in either order. Void outcomes may still be relayed at any time and close betting immediately, since a bet ordered ahead of a void only gets refunded. The signed Outcome struct is unchanged. The proof now passes on the result, and two new tests in `test/BetPoolSafety.t.sol` cover the held-ticket sequence and the exact boundaries.\n- **Expire versus late settle race (low).** Reproduced. `settle` now reverts with `SettlementWindowClosed` from `closesAt + 24 h` on, for every outcome, so expiry is the only finalization after that and a loser cannot front-run a late signed result. The previous test asserting late settlement works was replaced by one checking both sides of the boundary.\n- **Unset signer at deployment (low).** Reproduced, and I independently confirmed on Ethereum mainnet that GameEscrow's `signer()` is the EOA 0x3B59…7497 and its `owner()` is the brief's owner. The constructor now stores that address as the constant `INITIAL_SIGNER` and emits `SignerSet`, with no external call. `setSigner` remains for rotation, and the unreachable `SignerNotConfigured` error was removed.\n\n**Disputed, left as designed**\n\n- **Owner's setSigner power (info).** This is the power the brief grants. Per-market snapshots or delayed rotation would stop the owner from cutting off a compromised signer on open markets. The README trust section now states the immediate-effect consequence plainly.\n- **ZeroShares under dust minima (info).** The brief's `setLimits` rule is `min > 0`, so enforcing a 20-wei floor would refuse a setting the brief allows. The deployed minima are far above the threshold and the behaviour is already tested and documented.\n\n**Verification**\n\n| Check | Result |\n| --- | --- |\n| `forge build` | passes |\n| `forge test` with the verifier's fuzz seed | 90 passed, 0 failed |\n| Reviewer proof copied to `test/scratch/` | passes |\n| `forge fmt --check` on `src/`, `test/` | passes |\n\nREADME and `docs/REVIEW.md` now document the settlement window, the operator's responsibility for choosing `closesAt` before the result is known and relaying within 24 hours, and the owner's step to confirm the initial signer against GameEscrow before opening.","treeHash":"0f1c8da808919b63f158cdfb4cb96b148b5f78fb","usage":{"cachedInputTokens":1363511,"inputTokens":386,"model":"claude-fable-5-1","outputTokens":41379,"runtime":"claude","turns":63,"wallClockMs":668676}},{"artifacts":[],"attempt":1,"bundleHash":"d94bb1557de3f0d25e100a1e47b596ddc5f5c58ddb47bf3887d7547726c99aa1","device":"0e3b71e2ffcd200b","findings":[],"hash":"61c5ea96f51bd3b956db6d3bdf6f0da7646b12e021fb9355548f27fbedb2bd3b","nodeId":"f659562b-ac1c-4a9d-8600-2e593a5caf3e","outcome":"completed","summary":"The manifest is updated and verified. Only launch.json changed.\n\n**What was wrong.** The accepted source was revised after the previous manifest round. The constructor now stores a published NIGHTSHIFT signer constant and emits SignerSet, but my earlier notes said the signer starts unset and signed actions revert until the owner sets it. That description no longer matched the implementation.\n\n**What I changed.** The notes in launch.json now say the signer starts as the INITIAL_SIGNER constant that GameEscrow's signer() returned on mainnet, that it is a compile-time value rather than a live read, and that the owner may rotate it. The contract list is unchanged: one BetPool entry with an empty constructorArgs list, which matches the parameterless constructor in src/BetPool.sol.\n\n**Verification.**\n- The manifest has exactly the keys kind, contracts and notes. The notes are well under the 4000 character limit.\n- The forge build succeeded and the full test suite passed with the verifier's fuzz seed.\n\n| Check | Result |\n|---|---|\n| Test suites | 6 passed |\n| Tests | 87 passed, 0 failed |\n\nThe protected tests deploy BetPool with no arguments, which the empty constructorArgs list satisfies, and the owner is a static constant set to the brief's address rather than the factory.","treeHash":"40f8698da7b7a3d7ed83913c680cd9d3e5b78768","usage":{"cachedInputTokens":228370,"inputTokens":194,"model":"claude-fable-5-1","outputTokens":2830,"runtime":"claude","turns":7,"wallClockMs":206404}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"9df7d5d52e83c572","findings":[{"citation":"resolved","description":"settle() has no upper time bound: it stays callable at and after closesAt + REFUND_DELAY, the same instant from which expire() (line 258) becomes permissionless. Both paths flip market.settled, so whichever lands first is final (execution-trace: operation interleaving / wrong-state execution). Once the signer's Outcome transaction is visible in the mempool after that instant, any bettor on the losing side can front-run it with expire(marketId). The market is then void: the loser recovers their full stake, the winner receives only principal and no share of the losing side, the splitter receives no fee, and the signed outcome can never be applied (MarketAlreadySettled). The README documents 'the first successful finalization wins', but the consequence is that the economic result of a late-settled market is chosen by whichever party wins the gas race rather than by the signed result. Minimal fix that keeps the brief's semantics: in settle(), revert when block.timestamp >= uint256(market.closesAt) + REFUND_DELAY, so that after the delay the only finalization is the refund (the brief says an unsettled market 24h after closesAt becomes refundable; it does not require late signed settlement). Alternatively, if late signed settlement is wanted, restrict expire() to a longer delay than settle's window so the two cannot overlap. This is a design decision for the author.","line":236,"path":"src/BetPool.sol","reproduction":"Verified with test/scratch/ExpireRace.t.sol (passes on current code, showing the behavior). State: signer set; market ID created with token IMD, closesAt = T0 + 1h. winner bets side 0, 100e18, weight 10000; loser bets side 1, 100e18, weight 10000. Warp to closesAt + 24h. Signer signs Outcome(ID, 0, deadline = now + 10 min). Loser calls expire(ID) first: succeeds (block.timestamp == closesAt + REFUND_DELAY passes the check at line 258). Then settle(ID, 0, deadline, sig) reverts MarketAlreadySettled. Expected per the signed result: claimable(ID, winner) = 100e18 + 95e18 = 195e18, claimable(ID, loser) = 0, fee 5e18 to the splitter. Actual: claimable(ID, winner) = 100e18, claimable(ID, loser) = 100e18, fee 0.","severity":"low","snippet":"        if (deadline < block.timestamp) revert OutcomeExpired();","title":"After the 24h refund delay a losing bettor can front-run a late settle() with expire() and turn a loss into a refund"},{"citation":"resolved","description":"The brief says results come from NIGHTSHIFT's result signer 'as in its GameEscrow 0xf460fd9ad76f18414f54a2431870e8a116193489', and the launch recipe requires applications to be configured fully in the constructor with no post-deployment initialization call. The constructor leaves signer at address(0), so _verify (line 336) reverts SignerNotConfigured for createMarket, bet and settle on the freshly launched contract; only expire and claim work, and there is nothing to expire or claim. The README defers this to a manual owner transaction. The signer is not unknown: on Ethereum mainnet (chain id 1) GameEscrow.signer() returns 0x3B5919586FC04F7e99b450a72bcB14Eeb6977497 and GameEscrow.owner() returns 0x6C4c78f2C3bDf4C4318Fe305FB1C8BC1694d19b5, the same owner the brief names for BetPool (read via two independent public RPC endpoints, ethereum-rpc.publicnode.com and eth.drpc.org, on 2026-10-19). Minimal fix preserving the design: initialize signer in the constructor to that GameEscrow signer (emit SignerSet) and keep setSigner for rotation; or, if the author prefers not to hard-code it, document in the manifest notes that the launch is non-functional until the owner's setSigner lands and have the owner confirm the value against GameEscrow.signer(). No funds are at risk; the impact is a dead contract between launch and the owner's transaction, and a wrong manual value would route every market result to a key that is not NIGHTSHIFT's.","line":71,"path":"src/BetPool.sol","reproduction":"State: fresh deployment, no owner transaction. Call signer(): returns 0x0000000000000000000000000000000000000000. Call createMarket(keccak256('m'), 0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7, uint64(block.timestamp + 1 hours), keccak256('NIGHT CHESS'), sig) with sig produced by the GameEscrow signer 0x3B5919586FC04F7e99b450a72bcB14Eeb6977497 over the correct EIP-712 digest: expected (per the brief) MarketCreated; actual revert SignerNotConfigured() from _verify at line 336, regardless of the signature. The same holds for bet() and settle().","severity":"low","snippet":"    address public signer;","title":"Deployed BetPool has no signer: every signed entry point reverts until the owner sends a post-deployment setSigner(), although the NIGHTSHIFT signer is readable from GameEscrow"}],"hash":"836af2e69773a3d7d8b62f85cb2b9301f27514a846501e3393ce9cc3937bb591","nodeId":"456c9595-14ee-4adf-ba4f-fcf676e9efaf","outcome":"completed","summary":"The findings file is valid, both cited lines match the source exactly, the scratch directory is removed, and git reports the working tree unchanged apart from the untracked findings file.\n\n**Review complete.** The BetPool contract's control flow is sound in the three guides assigned (Execution Trace, Periphery, First Principles). I found no loss-of-funds defect. All 85 project tests pass locally, including the invariant campaign.\n\n**Findings written to .imd-findings.json**\n\n1. **Low: late-settlement race with expire().** The settle function has no upper time bound, so at and after closesAt plus 24 hours both settle and expire are live. A losing bettor who sees the signer's late Outcome transaction in the mempool can front-run it with expire, voiding the market. The loser then recovers their full stake and the winner gets only principal. I verified this sequence with a scratch test. The suggested fix is to make settle revert once the refund delay has elapsed, which keeps the brief's refund rule and removes the overlap.\n\n2. **Low: signer unconfigured at launch.** The constructor leaves the signer at zero, so createMarket, bet and settle all revert with SignerNotConfigured until the owner sends a manual setSigner. The brief ties results to the signer used by GameEscrow, and two independent public RPC endpoints return that contract's signer and show its owner is the same address the brief names for BetPool. The signer could be initialized in the constructor while keeping setSigner for rotation.\n\n**Coverage.** All ten ABI entry points have a row. Seven hold. createMarket, settle and expire reference the two findings above. Three extra rows record the conservation invariant, the vendored OpenZeppelin periphery, and signed-field replay binding, all of which hold. The slither and aderyn reentrancy and unused-return leads were traced and do not reach harm: state is written before the token call under the reentrancy guard, and the ECDSA error value is checked.\n\n**Not reached.** Nothing within the assigned area was left unexamined. I did not re-derive the fuzz suite's reference payouts by hand beyond spot-checking the formula against the spec.","treeHash":null,"usage":{"cachedInputTokens":717421,"inputTokens":290,"model":"claude-fable-5-1","outputTokens":24556,"runtime":"claude","turns":25,"wallClockMs":543958}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"803eeae3d8b63de3","findings":[],"hash":"90c19e8c2eef25f13b827159bf18a768daab66247b9d7f80c95a691932ba5108","nodeId":"b8c9613b-7bda-47a1-84a1-45bbff960d44","outcome":"completed","summary":"Review complete. The revised BetPool settles every finding from the last round, and I found nothing new to report. The findings file holds an empty list and a 13-row coverage record.\n\n**Prior reproducible finding, confirmed fixed**\n\n- **Held ticket ahead of the Outcome (medium).** The author took option (a): a decisive outcome now reverts BettingStillOpen while the block time is before closesAt, and bets are refused from closesAt. My attached proof passes on the revised code, and the economics specialist's proof fails only because the settle it expected to succeed now reverts. My own boundary tests confirmed closesAt minus one accepts a bet but refuses a decisive settle, and closesAt refuses the bet and accepts the settle. Void stays relayable at any time and a bet ordered ahead of it is simply refunded.\n\n**Prior advisory findings**\n\n- **expire front-running a late settle (low).** Fixed. settle now reverts SettlementWindowClosed at and after closesAt plus 24 hours, in either transaction order. At one second earlier settle works and expire reverts TooEarlyToExpire, so the two paths never overlap.\n- **No signer at deployment (low).** Fixed. The constructor stores the GameEscrow signer as a constant and emits SignerSet, with no external call. A fresh deployment returns that address from signer().\n- **Owner setSigner as settlement power (info).** Dispute accepted. The sequence still executes, but the brief grants immediate rotation, and the README now states the owner and signer keys must be held by the same party. Dropped.\n- **ZeroShares under sub-20-wei minima (info).** Dispute accepted. The brief's own setLimits rule allows min of 1, the revert is the safe choice, and deployed minima make it unreachable. Dropped.\n\n**Verification run**\n\n- The full project suite passed 108 of 108 tests, including both invariant suites.\n- The diff since last round touches only the two settle guards, the initial signer, and comments. I read each changed line and found no regression.\n- Static leads on reentrancy and the unused tryRecover value do not reproduce: tokens are fixed plain ERC-20 constants, bet is guarded with state written before the transfer, and the error enum is checked.\n\nOne residual worth the operator's attention, already documented in the README: the fix relies on NIGHTSHIFT choosing closesAt before a result can be known, since a ticket placed in the last block before closesAt by someone who already knows the result is still accepted. That is inherent to the design option I recommended, not a defect in the code.","treeHash":null,"usage":{"cachedInputTokens":1178248,"inputTokens":514,"model":"claude-fable-5-1","outputTokens":17013,"runtime":"claude","turns":17,"wallClockMs":720839}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"559cfaaab2c0d013","findings":[{"citation":"resolved","description":"settle() is permissionless and has no relation to closesAt; bet() accepts any signer-issued ticket until the block in which market.settled flips (checks at src/BetPool.sol:199-200 and the 120 s window at :205). Nothing separates the last accepted bet from the Outcome: there is no close step and bets carry no issue time the Outcome could exclude. A spectator who obtains a ticket for each side while the match is open (betting both sides is allowed) holds both and submits only the winning one once the result is known, either from watching the last two minutes of play or from seeing the Outcome relay in the mempool, ordered ahead of settle() in the same block. The bet carries zero risk and takes a weighted share of the losing pool, diluting every honest winner. Operator discipline (stop issuing tickets when the result is known) cannot close the window because a ticket stays valid for up to 120 s after issuance. Reproduced on this tree: the economics specialist's proof and my own fix-tolerant proof (test/scratch/HeldTicketRace.t.sol) both fail with attacker payout 579166666666666666666 > 500000000000000000000. Severity medium: principal is never at risk and the brief itself allows settle at any time with 120 s tickets, but honest winners lose most of their bonus to a risk-free actor under ordinary operation (an outcome relayed within two minutes of the last ticket). The fix needs a scope decision because the brief says settling itself closes betting: (a) require block.timestamp >= closesAt in settle() so the signer sets closesAt to the expected end of play and no bet can share a block with an outcome, or (b) add a signed close step / a betting cutoff inside the Outcome that excludes bets accepted at or after it, and shorten MAX_TICKET_LIFETIME if result latency cannot be bounded below 120 s. The attached proof passes under (a) (settle reverts, nothing claimable) or under any fix that pays a same-block late bet no bonus.","line":232,"path":"src/BetPool.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {BetPool} from \"src/BetPool.sol\";\n\ncontract ProofToken is ERC20 {\n    constructor() ERC20(\"T\", \"T\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\n/// @notice A ticket issued while the result was open is submitted in the same block as, and ahead of,\n///         the signed Outcome. On the current code the bet is accepted, the market settles in the same\n///         block, and the holder is paid a share of the losing pool for a bet that carried no risk.\n///         The assertion is fix-tolerant: it passes if the settlement cannot land in the same block as\n///         the bet (settle reverts), or if a bet landing in the settlement block earns no bonus.\ncontract HeldTicketRaceTest is Test {\n    address constant OWNER = 0x6C4c78f2C3bDf4C4318Fe305FB1C8BC1694d19b5;\n    address constant IMD = 0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7;\n    address constant SPLITTER = 0x1119d5Df2f046bC2d3c0F6279EF2E9cE49422d44;\n    uint256 constant SIGNER_KEY = 0xA11CE;\n    bytes32 constant ID = keccak256(\"night chess 7\");\n    bytes32 constant DOMAIN_TYPEHASH =\n        keccak256(\"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\");\n\n    BetPool pool;\n    address honestA = makeAddr(\"honestA\");\n    address honestB = makeAddr(\"honestB\");\n    address attacker = makeAddr(\"attacker\");\n\n    function setUp() public {\n        vm.warp(1_800_000_000);\n        vm.chainId(1);\n        pool = new BetPool();\n        vm.prank(OWNER);\n        pool.setSigner(vm.addr(SIGNER_KEY));\n        vm.etch(IMD, address(new ProofToken()).code);\n    }\n\n    function _sign(bytes32 structHash) internal view returns (bytes memory) {\n        bytes32 domain = keccak256(\n            abi.encode(DOMAIN_TYPEHASH, keccak256(\"NightshiftBets\"), keccak256(\"1\"), uint256(1), address(pool))\n        );\n        bytes32 digest = keccak256(abi.encodePacked(\"\\x19\\x01\", domain, structHash));\n        (uint8 v, bytes32 r, bytes32 s) = vm.sign(SIGNER_KEY, digest);\n        return abi.encodePacked(r, s, v);\n    }\n\n    function _ticket(address bettor, uint8 side, uint256 amount, uint16 w, uint64 expiry, bytes32 nonce)\n        internal\n        view\n        returns (bytes memory)\n    {\n        return _sign(keccak256(abi.encode(pool.TICKET_TYPEHASH(), ID, bettor, side, amount, w, expiry, nonce)));\n    }\n\n    function _bet(address bettor, uint8 side, uint256 amount, uint16 w, uint64 expiry, bytes32 nonce) internal {\n        ProofToken(IMD).mint(bettor, amount);\n        vm.startPrank(bettor);\n        IERC20(IMD).approve(address(pool), amount);\n        pool.bet(ID, side, amount, w, expiry, nonce, _ticket(bettor, side, amount, w, expiry, nonce));\n        vm.stopPrank();\n    }\n\n    function test_heldTicketSubmittedAheadOfOutcomeEarnsNoShareOfLosers() public {\n        uint64 closesAt = uint64(vm.getBlockTimestamp() + 1 hours);\n        pool.createMarket(\n            ID, IMD, closesAt, bytes32(\"chess\"),\n            _sign(keccak256(abi.encode(pool.MARKET_TYPEHASH(), ID, IMD, closesAt, bytes32(\"chess\"))))\n        );\n        // t0: honest bettors, 100 IMD each side at full weight.\n        _bet(honestA, 0, 100 ether, 10_000, uint64(vm.getBlockTimestamp() + 120), bytes32(\"hA\"));\n        _bet(honestB, 1, 100 ether, 10_000, uint64(vm.getBlockTimestamp() + 120), bytes32(\"hB\"));\n\n        // t0: result still open; the attacker obtains one ticket per side and submits neither.\n        uint64 expiry = uint64(vm.getBlockTimestamp() + 120);\n        bytes memory ticketA = _ticket(attacker, 0, 500 ether, 10_000, expiry, bytes32(\"atkA\"));\n        bytes memory ticketB = _ticket(attacker, 1, 500 ether, 10_000, expiry, bytes32(\"atkB\"));\n        ticketB; // never submitted\n\n        // t0 + 90 s: A has won; the signed Outcome is broadcast.\n        vm.warp(vm.getBlockTimestamp() + 90);\n        uint256 deadline = vm.getBlockTimestamp() + 1 hours;\n        bytes memory outcomeSig = _sign(keccak256(abi.encode(pool.OUTCOME_TYPEHASH(), ID, uint8(0), deadline)));\n\n        // Same block, ordered first: only the winning ticket is submitted. It is accepted.\n        ProofToken(IMD).mint(attacker, 500 ether);\n        vm.startPrank(attacker);\n        IERC20(IMD).approve(address(pool), 500 ether);\n        pool.bet(ID, 0, 500 ether, 10_000, expiry, bytes32(\"atkA\"), ticketA);\n        vm.stopPrank();\n\n        // Then the Outcome lands in the same block.\n        try pool.settle(ID, 0, deadline, outcomeSig) {\n            // Settlement in the same block as the late bet: that bet must not be paid from the losers.\n            uint256 attackerPayout = pool.claimable(ID, attacker);\n            uint256 honestPayout = pool.claimable(ID, honestA);\n            emit log_named_decimal_uint(\"attacker principal\", 500 ether, 18);\n            emit log_named_decimal_uint(\"attacker payout   \", attackerPayout, 18);\n            emit log_named_decimal_uint(\"honestA payout    \", honestPayout, 18);\n            // Current code: 500 + 95 * 5000 / 6000 = 579.166.. IMD; honestA's bonus falls from 95 to 15.83.\n            assertLe(attackerPayout, 500 ether, \"risk-free late bet was paid from the losing pool\");\n        } catch {\n            // A fix that keeps bets and outcomes out of the same block (for example settle only at or\n            // after closesAt) makes the race impossible: nothing left to check.\n            assertEq(pool.claimable(ID, attacker), 0, \"unsettled market must have nothing claimable\");\n        }\n    }\n}","reproduction":"State: signer configured; market ID on IMD, closesAt = t0 + 1 h. t0: honestA bet(ID,0,100e18,10000,t0+120,hA); honestB bet(ID,1,100e18,10000,t0+120,hB). Attacker obtains Ticket(ID,attacker,0,500e18,10000,t0+120,atkA) and Ticket(ID,attacker,1,500e18,10000,t0+120,atkB), submits neither. t0+90: signer signs Outcome(ID,0,deadline=t0+90+1h) and settle(ID,0,deadline,sig) is broadcast. Attacker submits bet(ID,0,500e18,10000,t0+120,atkA,sigA) in the same block, ordered first: accepted (market unsettled, block.timestamp < closesAt, expiry >= block.timestamp). settle then succeeds: fee = 100e18/20 = 5e18, sharesA = 600e18. Expected (fair pari-mutuel): a bet placed once the result is known earns no share of the losers, so claimable(ID,attacker) <= 500e18 and claimable(ID,honestA) = 195e18. Actual: claimable(ID,attacker) = 500e18 + mulDiv(95e18,500e18,600e18) = 579166666666666666666; claimable(ID,honestA) = 115833333333333333333. Run: forge test --match-path test/scratch/HeldTicketRace.t.sol -vv (fails on this code).","severity":"medium","snippet":"    function settle(bytes32 marketId, uint8 outcome, uint256 deadline, bytes calldata sig) external nonReentrant {","title":"Held ticket can land in the same block as, and ahead of, the signed Outcome: a bet placed with the result known is paid from the losing pool (merged: audit_economics, audit_permissions, audit_math)"},{"citation":"resolved","description":"settle() has no upper time bound (src/BetPool.sol:232-237 check only settled, outcome range, deadline and signature), while expire() becomes permissionless at block.timestamp >= closesAt + REFUND_DELAY (line 258). Both flip market.settled, so the first to land is final and the signed outcome can never be applied afterwards (MarketAlreadySettled at :234). Because expire needs no signature and is cheaper, any bettor on the side that lost can watch the mempool for the signer's late settle() and send expire() with a higher priority fee: the loser recovers the full stake, winners get only principal, the splitter gets no fee. This follows the brief's wording (expire if unsettled 24 h after closesAt; settle any time) and README line 72 documents the race, so it is reported as a design-level consequence for a scope decision, not a code defect: NIGHTSHIFT must relay every outcome well inside 24 h of closesAt, or settle() should refuse once block.timestamp >= closesAt + REFUND_DELAY so that after the delay the refund is the only finalization (the brief does not require late signed settlement). Reproduced on this tree with the economics specialist's test_expireFrontRunsLateSettlement (passes, showing the sequence executes). The project test test_settlementAfterRefundDeadlineStillWorksUntilSomeoneExpires exercises only the settle side of the race.","line":258,"path":"src/BetPool.sol","reproduction":"State: market ID (IMD) with closesAt = T; honestA bet 100e18 on A, honestB bet 100e18 on B, both weight 10000. Warp to T + 24 h. Signer signs Outcome(ID,0,deadline = now + 1 h) and settle(ID,0,deadline,sig) is broadcast. honestB calls expire(ID) first: succeeds (block.timestamp == closesAt + REFUND_DELAY passes line 258). Then settle(ID,0,deadline,sig) reverts MarketAlreadySettled. Expected per the signed result: claimable(ID,honestA) = 195e18, claimable(ID,honestB) = 0, splitter receives 5e18. Actual: claimable(ID,honestA) = 100e18, claimable(ID,honestB) = 100e18, splitter receives 0, and the outcome can never be applied.","severity":"low","snippet":"        if (block.timestamp < uint256(market.closesAt) + REFUND_DELAY) revert TooEarlyToExpire();","title":"From closesAt + 24 h both finalization paths are open: a losing bettor can front-run a late settle() with expire() and void the signed result (merged: audit_flow, audit_permissions, audit_economics)"},{"citation":"resolved","description":"The constructor (src/BetPool.sol:129-132) sets limits only; signer stays address(0), and _verify (line 336) reverts SignerNotConfigured for every signed entry point. The brief says results come from NIGHTSHIFT's result signer 'as in its GameEscrow 0xf460fd9ad76f18414f54a2431870e8a116193489', and the launch recipe asks for applications configured fully in the constructor with no post-deployment initialization call. Reading the escrow from the constructor would revert on the empty launch chain, so the author's choice is defensible, but the value is not unknown: I read it on Ethereum mainnet on 2026-10-19 via cast against ethereum-rpc.publicnode.com: GameEscrow.signer() = 0x3B5919586FC04F7e99b450a72bcB14Eeb6977497 and GameEscrow.owner() = 0x6C4c78f2C3bDf4C4318Fe305FB1C8BC1694d19b5, the same owner the brief names for BetPool (the IMD and SHIFT addresses answer symbol() 'IMD' and 'SHIFT', and the fee splitter has code). No funds are at risk; the impact is a dead contract between launch and the owner's transaction, and a wrong manual value would route every market result to a key that is not NIGHTSHIFT's. Minimal fix preserving the design: initialize signer in the constructor to that GameEscrow signer (emit SignerSet) and keep setSigner for rotation; otherwise the launch handoff must record setSigner as a required step and the owner must confirm the value against GameEscrow.signer(). launch.json's notes already state the contract is non-functional until setSigner.","line":71,"path":"src/BetPool.sol","reproduction":"State: fresh deployment, no owner transaction. signer() returns 0x0000000000000000000000000000000000000000. Call createMarket(keccak256('m'), 0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7, uint64(block.timestamp + 1 hours), keccak256('NIGHT CHESS'), sig) with sig produced by 0x3B5919586FC04F7e99b450a72bcB14Eeb6977497 over the correct EIP-712 digest: expected MarketCreated; actual revert SignerNotConfigured() at line 336 regardless of the signature. The same holds for bet() and settle(). Covered by the project test test_constructorDefaultsAndUnconfiguredSigner.","severity":"low","snippet":"    address public signer;","title":"Deployed BetPool has no signer: createMarket, bet and settle revert until the owner sends a post-deployment setSigner(), although the NIGHTSHIFT signer is readable from GameEscrow (merged: audit_flow,"},{"citation":"resolved","description":"The brief grants the owner setSigner, so this is an intentional power, not a permission bypass. It is recorded because the brief also says the owner must never redirect bets or settle without the signer, and the two rules are in tension: signer is a single global slot (line 138) with no delay and no per-market binding, so the owner can at any moment appoint a key it controls, have that key issue itself max-weight tickets on open markets, sign any Outcome for markets already holding other users' stakes (line 237 accepts whatever the current signer signs), or invalidate a pending honest Outcome by rotating just before it is relayed. The protocol's safety rests on the owner key and the signer key being held by the same trusted party; the README discloses this. Design-preserving hardening for the author to weigh: snapshot the signer into the Market struct at creation so rotation affects only later markets, and/or a delayed rotation. Both change when setSigner takes effect and need a scope decision.","line":136,"path":"src/BetPool.sol","reproduction":"State: signer S configured; market ID (IMD, closesAt now + 1 h); alice bets 500e18 on A at 10000 bps, bob bets 500e18 on B at 10000 bps (tickets signed by S). (1) owner calls setSigner(K), K a key the owner controls; (2) owner submits bet(ID,1,500e18,10000,expiry,nonce,sigK) signed by K for bettor = owner; (3) anyone submits settle(ID,1,deadline,sigK) signed by K regardless of the real result; (4) owner calls claim(ID). Expected per the brief's owner restrictions: no owner action can decide an outcome. Actual: every call succeeds; owner receives 500e18 + mulDiv(475e18,500e18,1000e18) = 737.5e18, alice's claimable is 0, 25e18 goes to the splitter.","severity":"info","snippet":"    function setSigner(address newSigner) external nonReentrant onlyOwner {","title":"Trust assumption: the owner's setSigner power is settlement power over funded markets (merged: audit_permissions, write_foundry_tests)"},{"citation":"resolved","description":"shares = amount * weightBps / 10000 (line 214) floors to zero whenever amount * weightBps < 10000. The brief says a ticket is accepted when the amount is within limits and weightBps is 500..10000, and setLimits permits min = 1 wei, so under such limits a validly signed, in-limit ticket is rejected. The revert is the safe choice (a nonempty winning side with zero shares would have no defined pro-rata payout), the deployed minima (0.1 IMD, 1000 SHIFT) make the branch unreachable, and the README and NatSpec document it. Informational: the precondition is a deliberate owner action and the only effect is a refused bet. The brief's rule could be stated as min >= 20 base units, or setLimits could enforce it so the two rules cannot diverge.","line":217,"path":"src/BetPool.sol","reproduction":"Owner calls setLimits(IMD, 1, 100) (accepted). Market ID on IMD open. Bettor presents a signer-valid ticket amount = 1, weightBps = 500: bet() reverts ZeroShares although 1 is within [1,100] and 500 is in range; the nonce is not consumed. amount = 20, weightBps = 500 is accepted with 1 share. Covered by the project test test_minimumCanBeOneWeiButZeroShareTicketCannotCreateUndefinedPayout.","severity":"info","snippet":"        if (shares == 0) revert ZeroShares();","title":"Under owner-set minima below 20 base units, an in-limit, in-range ticket is refused with ZeroShares"}],"hash":"a0358574ba50e5b86b9f5089cf3682420f8fb80809266fd49aae7d17bed1bfa6","nodeId":"b8c9613b-7bda-47a1-84a1-45bbff960d44","outcome":"completed","summary":"The review is complete. The findings file is written at the repository root, valid JSON, and no tracked file was changed.\n\n**Outcome.** The code is sound: all 104 project tests pass with the supplied seed, the runtime is 7,927 bytes with no forbidden opcodes, the constructor makes no external calls, and the manifest matches the no-argument constructor. The nine specialist reports collapsed into five findings, none critical or high.\n\n**Findings kept, after reproduction**\n\n- **Medium, held-ticket race.** A ticket issued while the result is open stays valid for up to 120 s and can be submitted in the same block as, and ahead of, the signed Outcome. The attacker takes a weighted share of the losing pool with zero risk. Both the economics specialist's proof and my own fix-tolerant proof fail on this code with payout 579.17 IMD on a 500 IMD bet. My proof is attached and passes under either plausible fix. Three specialists reported this; merged into one.\n- **Low, expire versus late settle.** From closesAt plus 24 hours both finalization paths are open, so a loser can front-run a late settle with expire and void the result. Reproduced with the specialist's scratch test. The brief's wording allows it; three reports merged.\n- **Low, signer unset at deployment.** Signed entry points revert until the owner calls setSigner. I confirmed on mainnet that GameEscrow's signer is 0x3B59…7497 and its owner equals the brief's owner, so the value could be hard-coded. Two reports merged.\n- **Info, owner setSigner equals settlement power.** Intentional per the brief, recorded as a trust assumption. Two reports merged.\n- **Info, ZeroShares refusal** under owner-set minima below 20 base units. Covered by an existing project test.\n\n**Dropped or recalibrated.** Nothing was dropped outright. The economics specialist's medium stands at medium; the permissions specialist's same-root-cause low and the flow specialist's low were folded into it. The slither reentrancy lead is a false positive: tokens are fixed plain ERC-20s, the guard is on, and the balance check is read before state changes only to verify exact receipt.\n\n**Coverage.** All ten entry points answered, plus rows for the conservation invariant and factory deployment. Nothing was left unreached.","treeHash":null,"usage":{"cachedInputTokens":777753,"inputTokens":290,"model":"claude-fable-5-1","outputTokens":21865,"runtime":"claude","turns":20,"wallClockMs":460117}},{"artifacts":[],"attempt":1,"bundleHash":"a72746f8652949675c098f3d02aaffc8ad428a4f58087e7bbc11a5f740f16de5","device":"8a0937587f04a986","findings":[],"hash":"b64ca97f5339a2b3a41c711bb729d5e0ab0a74fb62805bd3871cfa9525be6940","nodeId":"2c67bb99-aa28-4482-b488-22cfc6182819","outcome":"completed","summary":"Implemented [BetPool](/home/agent/.identitymd/work/2c64e994-a744-4249-b94f-66345c16b289/2c67bb99-aa28-4482-b488-22cfc6182819/src/BetPool.sol), vendored dependencies, tests, and [launch documentation](/home/agent/.identitymd/work/2c64e994-a744-4249-b94f-66345c16b289/2c67bb99-aa28-4482-b488-22cfc6182819/README.md).\n\nVerified:\n\n- `forge build` passed.\n- Seeded `forge test`: **85 passed**, including 12,288 invariant calls.\n- `forge fmt --check` passed.\n- CREATE2 deployment rehearsal passed.\n\nReady for IdentityMD’s deployment handoff; no live transaction was broadcast. The owner must configure the actual NIGHTSHIFT signer before use. Tickets that round to zero shares are rejected, as documented.","treeHash":"42c51d522f001edcfd806227361fb5a0ae4c1c30","usage":{"cachedInputTokens":1607168,"inputTokens":104165,"model":"gpt-6-astra","outputTokens":55562,"runtime":"codex","turns":9,"wallClockMs":1412123}},{"artifacts":[],"attempt":1,"bundleHash":"42bfc49952275609f09f2bd4f5544e5af690fe3c54002f8c1f787f70c94c85e8","device":"232b62e021f6f394","findings":[{"description":"The brief says results come from NIGHTSHIFT's result signer, as in GameEscrow 0xf460fd9ad76f18414f54a2431870e8a116193489. The constructor takes no signer, and signer starts as address(0). createMarket, bet and settle revert with SignerNotConfigured until the owner 0x6c4c78f2... calls setSigner. Reading the escrow from a constructor would revert on the empty launch chain, so this is defensible. It is still a required post-deploy step that the launch handoff must record.","line":71,"path":"src/BetPool.sol","reproduction":"Deploy new BetPool(); signer() == address(0); createMarket(...) with any signature reverts SignerNotConfigured (covered by test_constructorDefaultsAndUnconfiguredSigner).","severity":"info","title":"Signer is unset at deployment; every signed action reverts until the owner calls setSigner"},{"description":"The brief says the owner may setSigner but may never settle without the signer. These two rules are in tension: an owner can rotate the signer to a key they control and then sign any Outcome, including for markets that are already funded. The contract implements the brief as written, so this is a trust assumption, not a code defect. It should be disclosed to bettors. A timelock on setSigner would be a design change for the requester to decide.","line":136,"path":"src/BetPool.sol","reproduction":"Owner calls setSigner(ownerKeyAddr), then settle(id, chosenOutcome, deadline, sigByOwnerKey) succeeds. Expected per a strict reading of 'never settle without the signer': no single owner action can decide outcomes. Actual: two owner transactions can.","severity":"info","title":"The owner's setSigner power is equivalent to settlement power"}],"hash":"e237b435bfe486482a14ed9307497e0b09134550fd9501abf0462285660c7484","nodeId":"ae38615b-14b6-4469-8055-ccf2db4a660e","outcome":"completed","summary":"I added 18 unit and fuzz tests and an adversarial invariant suite on top of the existing 85 tests. With the verifier's seed, `forge build` succeeds and `forge test` passes all 104 tests, plus one throwaway check in `test/scratch/`. I found no defect in `src/BetPool.sol` that I could show with a failing test, so nothing is reported at critical, high or medium severity.\n\n**`test/BetPoolOracle.t.sol`**\n- **Hand-computed payouts:** one IMD market and one SHIFT market, each settled both ways. Odd amounts sit at and between the limits, weights range from 500 to 10000, and some wallets bet on both sides. The test checks the exact fee to the splitter, every claim down to the wei, zero for losers, and leftover dust of at most 1 wei per winner. I worked the expected numbers out in Python, outside the contract's formula.\n- **Void markets:** both markets voided and claimed with `claimMany` refund everyone exactly, and no fee is taken.\n- **Fee rounding on tiny losing sides:** 39 wei pays a 1 wei fee, 20 wei pays 1, and 19 wei pays nothing.\n- **Tickets that should be refused even with a valid signature:**\n  - amount one wei outside the limits for each token, and zero;\n  - weight 0, 499, 10001 or 65535;\n  - side 2 or 255, or an unknown market;\n  - a ticket reused for a different side, amount, weight, expiry or nonce;\n  - a ticket submitted by someone other than its bettor;\n  - a reused nonce.\n- **Time limits:** a ticket expiring 121 s ahead is refused, and an expired one is refused. Betting at `closesAt`, or after an early settlement, is refused. `expire` fails one second before `closesAt` + 24 h, works at that moment, and cannot run twice or be followed by `settle`.\n- **Bad signatures on settle and create:** settle rejects a signature for another market, outcome or deadline, a wrong key, and the owner without the signer. Create rejects mutated fields and tokens other than IMD and SHIFT.\n- **Claims, owner and pause:**\n  - claiming before settlement, twice, or with an empty batch;\n  - the owner gets nothing and every invalid `setLimits` is refused;\n  - strangers cannot use admin functions;\n  - pause blocks only new markets and bets.\n- **Fuzz tests:** a random key never authorises create, bet or settle. A single winner always gets their stake plus the losing side minus 5%, whatever the weights.\n\n**`test/BetPoolAttackInvariant.t.sol` and `test/helpers/BetPoolAttackHandler.sol`**\n- **Attacks in the random sequences:** honest calls are mixed with replayed tickets, stolen tickets, forged tickets, forged settlements sent by the owner, double settles and expiries, double claims, claims by strangers, early expiry, and admin calls by strangers. Each attack must be refused, leave balances unchanged, and leave refused nonces unused.\n- **Invariants:** for each token, the pool holds exactly what it owes, and no market pays out more than was bet. What remains to be claimed never exceeds a market's funds. The splitter holds exactly the fees, and settled markets never change. The attacker and the owner never receive tokens.\n- **Claim checks:** each claim is checked against bounds rather than the exact formula. A refund equals the stakes exactly. A wallet with no winning stake gets 0. A winner gets at least their stake and at most their stake plus the losing side minus the fee.\n- **Run size:** 64 runs at depth 64. The scratch check confirmed the handler's settle, claim and attack paths actually run.\n\n`.imd-findings.json` holds two info-level notes, both design points rather than code bugs:\n1. **No signer at deploy:** the signer is unset when the contract is deployed. Nothing can be created, bet or settled until the owner calls `setSigner`, so the launch handoff has to include that step.\n2. **Signer rotation equals settlement power:** the owner can point the signer at a key they control and then sign any outcome. This sits uneasily with \"never settle without the signer\"; the contract follows the brief as written. Whether to disclose this or add a timelock is t","treeHash":"3b582903893f034508b0eddf64ac648980d4837f","usage":{"cachedInputTokens":1316494,"inputTokens":36,"model":"claude-opus-5-5","outputTokens":33241,"runtime":"claude","turns":19,"wallClockMs":784478}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"ddc5542bddcad225","findings":[{"citation":"resolved","description":"Trust-gap (economics x asymmetry). settle() is permissionless and may land at any time before closesAt, while bet() accepts any signer-issued ticket until the very block in which the market becomes settled (the only time guard is `block.timestamp >= market.closesAt` at src/BetPool.sol:200 and the 120 s ticket lifetime at :205). The signed Outcome is therefore public information (in the mempool or relayed by the NIGHTSHIFT server) for the whole window during which tickets issued in the last 120 s of play are still valid. A bettor holding such a ticket on the side the Outcome names can submit it ahead of the settle transaction and collect a risk-free share of the losing pot; the minimum weight of 500 bps only caps the size of the share, it does not remove it. The server-side rule 'late bets on a clear leader weigh less' does not protect against this because the contract cannot tell a bet placed before the result from one placed after it was signed. This is behaviour the brief allows (settle 'at any time', tickets valid 120 s), so it is reported as a documented gap rather than a spec violation. Mitigations that keep the agreed design: operationally, the server stops issuing tickets at least 120 s before it signs an outcome and relays outcomes through a private channel; in-contract, a small change such as requiring `block.timestamp >= closesAt` for settle (or a signer-chosen `bettingEnd` in the Market message) would close the window but needs a scope decision because the brief says settling itself closes betting. The existing suite never exercises a bet and a settle of the same market in the same block, so this ordering is untested.","line":232,"path":"src/BetPool.sol","reproduction":"Setup: signer configured, market ID (IMD) created with closesAt = now + 1 h; alice bets 100 IMD on A at 10000 bps, bob bets 100 IMD on B at 10000 bps. Mallory requests and receives a signed Ticket(ID, mallory, side 0, 500 IMD, 500 bps, expiry = now + 120, nonce) but does not submit it. The signer signs Outcome(ID, 0, deadline = now + 60) and a relayer broadcasts settle(ID, 0, deadline, sig). In the same block, before that settle executes, Mallory calls bet(ID, 0, 500e18, 500, expiry, nonce, ticketSig). Expected (fair pari-mutuel): a bet placed after the result is known should not participate. Actual: bet succeeds (market not yet settled, expiry not passed), settle then succeeds, sharesA = 100e18 (alice) + 25e18 (mallory); mallory.claim(ID) pays 500e18 + mulDiv(95e18, 25e18, 125e18) = 519e18, i.e. 19 IMD of bob's stake with zero risk. Verified in a scratch Foundry test on this tree (claim returned exactly 519 ether).","severity":"low","snippet":"    function settle(bytes32 marketId, uint8 outcome, uint256 deadline, bytes calldata sig) external nonReentrant {\n        Market storage market = _market(marketId);\n        if (market.settled) revert MarketAlreadySettled();\n        if (outcome > 2) revert InvalidOutcome();\n        if (deadline < block.timestamp) revert OutcomeExpired();\n        _verify(keccak256(abi.encode(OUTCOME_TYPEHASH, marketId, outcome, deadline)), sig);","title":"Unexpired ticket can be submitted in front of the settlement that reveals the outcome (economics x asymmetry seam)"},{"citation":"resolved","description":"Access x economics x asymmetry. The brief grants the owner setSigner, so this is an intentional power and not a permission bypass; it is recorded as a material trust assumption because the brief also says the owner must 'never move or redirect bets' or 'settle without the signer'. Because `signer` is a single global slot with no delay and no per-market binding, the owner can at any moment make itself (or any key it controls) the result signer and then: (a) issue itself max-weight tickets on any open market, (b) sign any Outcome for markets that already hold other users' stakes, (c) invalidate a pending honest Outcome by rotating just before it is relayed, so the honest result never settles and the market falls back to a refund after 24 h. Each step is individually permitted; together they let the owner redirect the losing pot of an in-flight market to itself, which is exactly the outcome the brief's owner restrictions try to exclude. The protocol's safety therefore rests on the owner key and the signer key being held by the same trusted party. Design-preserving hardening options for the author to weigh: snapshot the signer into the Market struct at creation so rotation only affects markets created afterwards, and/or a two-step or time-delayed rotation. Both keep setSigner but change when it takes effect, so they need a scope decision.","line":136,"path":"src/BetPool.sol","reproduction":"Setup: signer S configured; market ID (IMD, closesAt now + 1 h); alice bets 500 IMD on A at 10000 bps, bob bets 500 IMD on B at 10000 bps (tickets signed by S). Sequence: (1) OWNER calls setSigner(K) where K is a key the owner controls; (2) OWNER submits bet(ID, 1, 500e18, 10000, expiry, nonce, sigK) signed by K for bettor = OWNER; (3) anyone submits settle(ID, 1, deadline, sigK) signed by K, regardless of the real match result; (4) OWNER calls claim(ID). Expected per the brief's owner restrictions: the owner cannot redirect bets or settle without NIGHTSHIFT's signer. Actual: every call succeeds; the owner receives 500e18 + mulDiv(475e18, 500e18, 1000e18) = 737.5 IMD, alice's claimable is 0, and the fee of 25 IMD goes to the splitter. Verified in a scratch Foundry test on this tree.","severity":"info","snippet":"    function setSigner(address newSigner) external nonReentrant onlyOwner {\n        if (newSigner == address(0)) revert InvalidSigner();\n        signer = newSigner;\n        emit SignerSet(newSigner);\n    }","title":"Owner can rotate the signer to a key it controls and decide in-flight markets, including ones it bet on (trust assumption, three-way seam)"},{"citation":"resolved","description":"Access x asymmetry. expire() is permissionless and needs no signature, settle() is permissionless and needs the signer's Outcome, and both are valid in the same state once 24 h have passed since closesAt. Whichever lands first is final (`market.settled` at :257/:234), and the contract treats them asymmetrically: expire pays everyone back with no fee, settle pays winners and takes 5 % from losers. A losing bettor who sees the late settle in the mempool wins the race by calling expire() first (cheaper, no signature), denying the winners their payout and the splitter its fee. This is what the brief asks for ('if unsettled 24 h after closesAt, anyone makes it refundable'), so it is a note about the operational consequence, not a defect: NIGHTSHIFT must settle within 24 h of closesAt or accept that any late Outcome can be front-run by a loser. The existing test test_settlementAfterRefundDeadlineStillWorksUntilSomeoneExpires shows the settle side of this race only.","line":258,"path":"src/BetPool.sol","reproduction":"Setup: market ID (IMD) with closesAt = T; alice bets 100 IMD on A, bob bets 100 IMD on B (both 10000 bps). Warp to T + 24 h. The signer signs Outcome(ID, 0, deadline = now + 60) ('A won') and a relayer broadcasts settle(ID, 0, deadline, sig). bob (the loser) submits expire(ID) with a higher priority fee. Expected (from the winner's point of view): alice receives 195 IMD, splitter receives 5 IMD. Actual: expire succeeds first; settle then reverts with MarketAlreadySettled; bob.claim(ID) returns his full 100 IMD, alice gets only her 100 IMD back and the splitter receives 0. Verified in a scratch Foundry test on this tree.","severity":"info","snippet":"        if (block.timestamp < uint256(market.closesAt) + REFUND_DELAY) revert TooEarlyToExpire();","title":"After closesAt + 24 h a losing bettor can front-run a valid settlement with expire() and convert the result into a refund"}],"hash":"f272fc00522dd5615a13c3c1b980c758a75e348a9a3c2f9e936a4a083fe9f542","nodeId":"92b2a859-cd17-45fd-9d76-c351bee8e561","outcome":"completed","summary":"The review is complete. The findings file is valid JSON, the scratch directory is removed, and `git status --porcelain` shows nothing outside the findings file.\n\n**Outcome.** In the assigned area (Access Control, Trust Gap, Asymmetry) the BetPool contract holds. The permission model is minimal and correct: the owner is a constant, its four powers match the brief, every signed path binds chain, contract and all fields, tickets bind the caller, and the settle/claimable formulas mirror each other exactly. I found no permission bypass, no missing guard, and no asymmetric accounting. I recorded three trust-boundary behaviors with exact reproductions, each confirmed by a scratch Foundry test on this tree:\n\n- **Low, ticket front-runs settlement.** A ticket issued in the last 120 seconds of play stays valid until the block in which settle lands. Whoever sees the signed Outcome can place a guaranteed-winning bet ahead of it. The suite never tests a bet and settle of the same market in one block.\n- **Info, owner becomes the signer.** The brief grants setSigner, so this is a trust assumption, not a bypass. Rotation is global and immediate, so the owner can decide markets that already hold other users' stakes. Suggested hardening keeps setSigner but snapshots the signer per market or delays rotation.\n- **Info, expire races a late settle.** After closesAt plus 24 hours, a loser can front-run a valid settlement with expire and force a refund. This is what the brief asks for, recorded for the operator.\n\n**Coverage.** All ten entry points have rows, plus two invariant rows. Seven are marked holds and three carry a finding reference. None are unreached.\n\n**Not done.** No critical or high findings exist, so no proof files were attached. The Economic, Math and Boundary guides were outside my area and only touched where they overlapped with the seams above.","treeHash":null,"usage":{"cachedInputTokens":588955,"inputTokens":258,"model":"claude-fable-5-1","outputTokens":22479,"runtime":"claude","turns":21,"wallClockMs":371003}}],"verification":[{"checks":[{"durationMs":21050,"exitCode":0,"name":"build","output":"Compiling 48 files with Solc 0.8.26\nSolc 0.8.26 finished in 20.98s\nCompiler run successful!\n","passed":true},{"durationMs":28329,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 3 tests for test/BetPoolEvents.t.sol:BetPoolEventsTest\n[PASS] test_adminAndExpiryEvents() (gas: 327618)\n[PASS] test_marketBetSettlementAndClaimEvents() (gas: 1063752)\n[PASS] test_refundableSettlementEmitsZeroFee() (gas: 551103)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 2.47ms (1.97ms CPU time)\n\nRan 21 tests for test/BetPoolFlows.t.sol:BetPoolFlowsTest\n[PASS] test_claimManyDuplicateRollsBackEntireBatch() (gas: 788988)\n[PASS] test_claimManyMixesTokensAndLosingClaims() (gas: 2224954)\n[PASS] test_claimManyUnknownOrUnsettledRollsBack() (gas: 879710)\n[PASS] test_constructorDefaultsAndUnconfiguredSigner() (gas: 126474)\n[PASS] test_emptyMarketCanSettleAllOutcomes() (gas: 723067)\n[PASS] test_exactPayoutsAndFeeForSimpleWeights() (gas: 1500087)\n[PASS] test_expireEmptyMarket() (gas: 210090)\n[PASS] test_expiryAtBoundaryRefundsAndFinalizes() (gas: 1477426)\n[PASS] test_fullWeightedFlowIMD_AWins() (gas: 3645653)\n[PASS] test_fullWeightedFlowIMD_BWins() (gas: 3646874)\n[PASS] test_fullWeightedFlowSHIFT_AWins() (gas: 3646454)\n[PASS] test_fullWeightedFlowSHIFT_BWins() (gas: 3646926)\n[PASS] test_losersAndNonBettorsReceiveNothingAndCannotClaimTwice() (gas: 1229993)\n[PASS] test_marketPotsAreIndependentAndDonationsDoNotChangePayouts() (gas: 1672859)\n[PASS] test_oddLosingAmountRoundsFeeDownAndOnlyBonusCreatesDust() (gas: 1440830)\n[PASS] test_oneSidedBRefundedEvenWhenAIsDeclaredWinner() (gas: 649596)\n[PASS] test_oneSidedLosingSideRefunded() (gas: 1076725)\n[PASS] test_oneSidedVoidRefunded() (gas: 1072781)\n[PASS] test_oneSidedWinningSideRefunded() (gas: 1076738)\n[PASS] test_voidRefundsBothSidesIMD() (gas: 1397004)\n[PASS] test_voidRefundsBothSidesSHIFT() (gas: 1397063)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 72.59ms (26.10ms CPU time)\n\nRan 27 tests for test/BetPoolSignatures.t.sol:BetPoolSignaturesTest\n[PASS] testFuzz_createRejectsWrongSignerContractChain(uint8) (runs: 512, μ: 56592, ~: 56665)\nLogs:\n  Bound result 1\n\n[PASS] testFuzz_createSignatureBindsEveryField(uint8) (runs: 512, μ: 56425, ~: 56430)\nLogs:\n  Bound result 1\n\n[PASS] testFuzz_malformedSignatureLengthsRejected(uint8) (runs: 512, μ: 61931, ~: 61763)\nLogs:\n  Bound result 49\n\n[PASS] testFuzz_outcomeSignatureBindsEveryField(uint8) (runs: 512, μ: 142179, ~: 142296)\nLogs:\n  Bound result 1\n\n[PASS] testFuzz_settleRejectsWrongSignerContractChain(uint8) (runs: 512, μ: 55172, ~: 55236)\nLogs:\n  Bound result 1\n\n[PASS] testFuzz_ticketSignatureBindsEveryField(uint8) (runs: 512, μ: 105149, ~: 84243)\nLogs:\n  Bound result 0\n\n[PASS] test_badVAndZeroRecoveryRejected() (gas: 116404)\n[PASS] test_createSignatureCannotBeReplayedOnNewDeployment() (gas: 99751)\n[PASS] test_creationAtNowPastAndTooFarRejected() (gas: 124513)\n[PASS] test_creationAtSevenDaysAndPermissionlessSubmission() (gas: 117388)\n[PASS] test_domainAndTypeHashesMatchExactSpecification() (gas: 41207)\n[PASS] test_domainChangesAfterChainIdChange() (gas: 322913)\n[PASS] test_duplicateMarketIdCannotBeReusedAfterSettlement() (gas: 145384)\n[PASS] test_expiredSettlementRejected() (gas: 44787)\n[PASS] test_highSMalleatedSignatureRejected() (gas: 290489)\n[PASS] test_nonceIsGlobalAcrossMarketsWalletsAndSignerRotations() (gas: 410248)\n[PASS] test_ownerCannotSettleWithoutSigner() (gas: 65451)\n[PASS] test_personalSignSignatureRejected() (gas: 65549)\n[PASS] test_rotationRevokesAllOldSignatureTypesAndNewSignerWorks() (gas: 583254)\n[PASS] test_settlementDeadlineInclusiveAndPermissionless() (gas: 95759)\n[PASS] test_ticketExpiredRejectedEvenWithValidSignature() (gas: 56353)\n[PASS] test_ticketExpiryAtCurrentTimeAndMaximumAccepted() (gas: 385256)\n[PASS] test_ticketNonceCannotBeReusedEvenWithDifferentSignedFields() (gas: 347765)\n[PASS] test_ticketTooFarExpiryRejectedEvenWithValidSignature() (gas: 56817)\n[PASS] test_ticketWrongChain() (gas: 65405)\n[PASS] test_ticketWrongContract() (gas: 65180)\n[PASS] test_ticketWrongSigner() (gas: 65121)\nSuite result: ok. 27 passed; 0 failed; 0 skipped; finished in 72.61ms (319.77ms CPU time)\n\nRan 31 tests for test/BetPoolSafety.t.sol:BetPoolSafetyTest\n[PASS] test_IMDAmountsOutsideLimitsRejected() (gas: 251662)\n[PASS] test_SHIFTAmountsOutsideLimitsRejected() (gas: 251857)\n[PASS] test_bettingExclusiveCloseBoundary() (gas: 591849)\n[PASS] test_duplicatePauseAndUnpauseRejectedAndConfigurationWorksWhilePaused() (gas: 222573)\n[PASS] test_factoryDeploymentWithoutAnyExternalContractsOrPrivileges() (gas: 2165504)\n[PASS] test_failedClaimPreservesEntitlementAndDoesNotBlockOthers() (gas: 1257451)\n[PASS] test_failedFeeTransferRollsBackSettlementAndCanExpireLater() (gas: 1302548)\n[PASS] test_inexactTransfersRevertAndRollBackEverything() (gas: 1311330)\n[PASS] test_insufficientAllowanceAndBalanceRollBack() (gas: 779492)\n[PASS] test_invalidOutcomeEvenIfSignedRejected() (gas: 131337)\n[PASS] test_invalidSideAndWeightRejectedEvenIfSignerApproves() (gas: 315445)\n[PASS] test_limitsCanReachButNeverExceedEitherHardCap() (gas: 276307)\n[PASS] test_minimumCanBeOneWeiButZeroShareTicketCannotCreateUndefinedPayout() (gas: 1059123)\n[PASS] test_newLimitsApplyToPendingTicketsAndExistingMarketsOnlyProspectively() (gas: 1094218)\n[PASS] test_noClaimsBeforeSettlementAndNoBetsOrDoubleSettleAfterwards() (gas: 691919)\n[PASS] test_onlyOwnerMayConfigureOrPause() (gas: 123335)\n[PASS] test_onlyTwoTokensAccepted() (gas: 112165)\n[PASS] test_ownerHasNoCustodyRedirectionOrOwnershipTransferFunctions() (gas: 740882)\n[PASS] test_pauseBlocksOnlyNewMarketsAndBets() (gas: 2252258)\n[PASS] test_reentrantBetCallbackIsBlocked() (gas: 681798)\n[PASS] test_reentrantClaimAndClaimManyCallbacksAreBlocked() (gas: 1386208)\n[PASS] test_reentrantSettlementFeeCallbackIsBlocked() (gas: 1123521)\n[PASS] test_runtimeSizeAndNoForbiddenOpcodes() (gas: 4447414)\n[PASS] test_safeERC20AcceptsNoReturnData() (gas: 1080076)\n[PASS] test_settleAfterExpireRejected() (gas: 207722)\n[PASS] test_settlementAfterRefundDeadlineStillWorksUntilSomeoneExpires() (gas: 1057974)\n[PASS] test_signerCannotBeSetToZero() (gas: 43239)\n[PASS] test_tokenWithoutCodeCannotAcceptBet() (gas: 169842)\n[PASS] test_transferFromFailureDoesNotConsumeNonceOrRecordBet() (gas: 1103820)\n[PASS] test_twentyWeiMinimumWorksAtSmallestWeight() (gas: 1076278)\n[PASS] test_unknownMarketsRejectAllMutationsAndViewsReturnZero() (gas: 196772)\nSuite result: ok. 31 passed; 0 failed; 0 skipped; finished in 199.20ms (29.95ms CPU time)\n\nRan 2 tests for test/BetPoolFuzz.t.sol:BetPoolFuzzTest\n[PASS] testFuzz_limitsWithinHardCapsNeverOverflowShares(bool,uint128,uint16,bool) (runs: 512, μ: 1091431, ~: 1091400)\nLogs:\n  Bound result 3600\n  Bound result 755\n\n[PASS] testFuzz_payoutsFollowWeightedSharesAndConserveBothTokens(uint128[8],uint16[8],bool,uint8,bool,bool) (runs: 512, μ: 5466740, ~: 6595895)\nLogs:\n  Bound result 1\n  Bound result 24999000000000000000010000\n  Bound result 9505\n  Bound result 24999029000000000000000001\n  Bound result 5000\n  Bound result 24999000000000000000018424\n  Bound result 5669\n  Bound result 25000000000000000000000000\n  Bound result 9507\n  Bound result 24999009650000000000000001\n  Bound result 9600\n  Bound result 24999000000000000000001412\n  Bound result 6401\n  Bound result 25000000000000000000000000\n  Bound result 2724\n  Bound result 24999000000000000000009495\n  Bound result 9522\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 199.22ms (270.14ms CPU time)\n\nRan 1 test for test/BetPoolInvariant.t.sol:BetPoolInvariantTest\n[PASS]\nBetPoolInvariantTest invariants:\n[PASS] invariant_actualBalancesCoverAllMarketsAndConserveEveryWei\n[PASS] invariant_authorityAndNoncesRemainCorrect\n[PASS] invariant_marketStateAndEveryClaimMatchIndependentModel\n BetPoolInvariantTest invariants (runs: 128, calls: 12288, reverts: 0)\n\n╭----------------+--------------+-------+---------+----------╮\n| Contract       | Selector     | Calls | Reverts | Discards |\n+============================================================+\n| BetPoolHandler | adjustLimits | 1107  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | advance      | 1165  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | claim        | 1106  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | claimMany    | 1069  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | create       | 1088  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | donate       | 1181  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | expire       | 1061  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | place        | 1193  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | rotateSigner | 1133  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | settle       | 1106  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | togglePause  | 1079  | 0       | 0        |\n╰----------------+--------------+-------+---------+----------╯\n\nLogs:\n  Bound result 10000000000000000000\n  Bound result 10000\n  Bound result 10000000000000000000\n  Bound result 5000\n  Bound result 10000000000000000000\n  Bound result 4000\n  Bound result 10000000000000000000\n  Bound result 500\n  Bound result 1000000000000000000000000\n  Bound result 10000\n  Bound result 1000000000000000000000000\n  Bound result 5000\n  Bound result 1000000000000000000000000\n  Bound result 4000\n  Bound result 1000000000000000000000000\n  Bound result 500\n  Bound result 10000000000000000000\n  Bound result 10000\n  Bound result 10000000000000000000\n  Bound result 5000\n  Bound result 10000000000000000000\n  Bound result 4000\n  Bound result 10000000000000000000\n  Bound result 500\n  Bound result 1000000000000000000000000\n  Bound result 10000\n  Bound result 1000000000000000000000000\n  Bound result 5000\n  Bound result 1000000000000000000000000\n  Bound result 4000\n  Bound result 1000000000000000000000000\n  Bound result 500\n  Bound result 895\n  Bound result 8251\n  Bound result 2987\n  Bound result 2363\n  Bound result 6716\n  Bound result 0\n  Bound result 5000000000000000000\n  Bound result 9995000000000000007382\n  Bound result 2900000000000000000\n  Bound result 4143\n  Bound result 11000000000000000000\n  Bound result 7531\n  Bound result 1800\n  Bound result 5001\n  Bound result 27\n  Bound result 4905\n  Bound result 120\n  Bound result 499999999999999999999999945\n  Bound result 0\n  Bound result 6722540745621196690441\n  Bound result 9832377763136409928935\n  Bound result 1085\n  Bound result 658\n  Bound result 6222\n  Bound result 13\n  Bound result 528174651\n  Bound result 603546\n  Bound result 394892196923676441702320755\n  Bound result 417807781869536290929843294\n  Bound result 2\n  Bound result 1356\n  Bound result 406076023262212748856756433\n  Bound result 8392\n  Bound result 601214\n  Bound result 405259479042112148068\n  Bound result 5327392583068602357338\n  Bound result 921\n  Bound result 12567\n  Bound result 9999999999999999997217\n  Bound result 8192\n  Bound result 601222\n  Bound result 405692699322060444063776314\n  Bound result 471536503389698387335956274\n  Bound result 253827\n  Bound result 1496\n  Bound result 2514000706\n  Bound result 3000000000000000000\n  Bound result 0\n  Bound result 10\n  Bound result 51525089824\n  Bound result 505667\n  Bound result 0\n  Bound result 20\n  Bound result 14706\n  Bound result 2900000000000000019\n  Bound result 8877\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 28.25s (28.24s CPU time)\n\nRan 6 test suites in 28.25s (28.80s CPU time): 85 tests passed, 0 failed, 0 skipped (85 total tests)\n","passed":true},{"durationMs":37,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"BetPool.bet(bytes32,uint8,uint256,uint16,uint64,bytes32,bytes)\",\"BetPool.claim(bytes32)\",\"BetPool.claimMany(bytes32[])\",\"BetPool.createMarket(bytes32,address,uint64,bytes32,bytes)\",\"BetPool.expire(bytes32)\",\"BetPool.pause()\",\"BetPool.setLimits(address,uint256,uint256)\",\"BetPool.setSigner(address)\",\"BetPool.settle(bytes32,uint8,uint256,bytes)\",\"BetPool.unpause()\"],\"files\":{\".gitignore\":4,\"LICENSE\":21,\"README.md\":104,\"docs/DEPENDENCIES.md\":12,\"docs/REVIEW.md\":56,\"docs/eip712-types.json\":28,\"foundry.toml\":22,\"launch.json\":10,\"remappings.txt\":2,\"src/BetPool.sol\":353,\"test/BetPoolEvents.t.sol\":67,\"test/BetPoolFlows.t.sol\":326,\"test/BetPoolFuzz.t.sol\":115,\"test/BetPoolInvariant.t.sol\":125,\"test/BetPoolSafety.t.sol\":481,\"test/BetPoolSignatures.t.sol\":333,\"test/helpers/BetPoolHandler.sol\":295,\"test/helpers/BetPoolTestBase.sol\":140,\"test/helpers/MockToken.sol\":76},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"38f091b913db03b03410e15b57af0030ba81d939abe12c6975065d7f15ab524e","verifiedTreeHash":"83fa49c600838ad16fbdf1ac88cb995f672b32a4","verifierVersion":"0.1.0+a6ee6548"},{"checks":[{"durationMs":86231,"exitCode":0,"name":"build","output":"Compiling 51 files with Solc 0.8.26\nSolc 0.8.26 finished in 86.06s\nCompiler run successful!\n","passed":true},{"durationMs":52410,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 3 tests for test/BetPoolEvents.t.sol:BetPoolEventsTest\n[PASS] test_adminAndExpiryEvents() (gas: 327742)\n[PASS] test_marketBetSettlementAndClaimEvents() (gas: 1064586)\n[PASS] test_refundableSettlementEmitsZeroFee() (gas: 576250)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 9.09ms (8.48ms CPU time)\n\nRan 27 tests for test/BetPoolSignatures.t.sol:BetPoolSignaturesTest\n[PASS] testFuzz_createRejectsWrongSignerContractChain(uint8) (runs: 512, μ: 56555, ~: 56619)\nLogs:\n  Bound result 2\n\n[PASS] testFuzz_createSignatureBindsEveryField(uint8) (runs: 512, μ: 56397, ~: 56394)\nLogs:\n  Bound result 3\n\n[PASS] testFuzz_malformedSignatureLengthsRejected(uint8) (runs: 512, μ: 59782, ~: 59615)\nLogs:\n  Bound result 36\n\n[PASS] testFuzz_outcomeSignatureBindsEveryField(uint8) (runs: 512, μ: 167335, ~: 167449)\nLogs:\n  Bound result 1\n\n[PASS] testFuzz_settleRejectsWrongSignerContractChain(uint8) (runs: 512, μ: 77864, ~: 77925)\nLogs:\n  Bound result 2\n\n[PASS] testFuzz_ticketSignatureBindsEveryField(uint8) (runs: 512, μ: 104576, ~: 84251)\nLogs:\n  Bound result 0\n\n[PASS] test_badVAndZeroRecoveryRejected() (gas: 112108)\n[PASS] test_createSignatureCannotBeReplayedOnNewDeployment() (gas: 82637)\n[PASS] test_creationAtNowPastAndTooFarRejected() (gas: 124513)\n[PASS] test_creationAtSevenDaysAndPermissionlessSubmission() (gas: 117374)\n[PASS] test_domainAndTypeHashesMatchExactSpecification() (gas: 41251)\n[PASS] test_domainChangesAfterChainIdChange() (gas: 322841)\n[PASS] test_duplicateMarketIdCannotBeReusedAfterSettlement() (gas: 169863)\n[PASS] test_expiredSettlementRejected() (gas: 67504)\n[PASS] test_highSMalleatedSignatureRejected() (gas: 288305)\n[PASS] test_nonceIsGlobalAcrossMarketsWalletsAndSignerRotations() (gas: 410198)\n[PASS] test_ownerCannotSettleWithoutSigner() (gas: 86041)\n[PASS] test_personalSignSignatureRejected() (gas: 65513)\n[PASS] test_rotationRevokesAllOldSignatureTypesAndNewSignerWorks() (gas: 610660)\n[PASS] test_settlementDeadlineInclusiveAndPermissionless() (gas: 118455)\n[PASS] test_ticketExpiredRejectedEvenWithValidSignature() (gas: 56353)\n[PASS] test_ticketExpiryAtCurrentTimeAndMaximumAccepted() (gas: 385184)\n[PASS] test_ticketNonceCannotBeReusedEvenWithDifferentSignedFields() (gas: 347751)\n[PASS] test_ticketTooFarExpiryRejectedEvenWithValidSignature() (gas: 56817)\n[PASS] test_ticketWrongChain() (gas: 65369)\n[PASS] test_ticketWrongContract() (gas: 65144)\n[PASS] test_ticketWrongSigner() (gas: 65085)\nSuite result: ok. 27 passed; 0 failed; 0 skipped; finished in 357.30ms (567.09ms CPU time)\n\nRan 18 tests for test/BetPoolOracle.t.sol:BetPoolOracleTest\n[PASS] testFuzz_randomKeyNeverAuthorizes(uint256,uint8) (runs: 256, μ: 417962, ~: 418104)\nLogs:\n  Bound result 3\n  Bound result 1\n\n[PASS] testFuzz_winnerGetsPrincipalPlusAtMostNetLosingSide(uint256,uint256,uint256,uint16,uint16,bool,bool) (runs: 256, μ: 1504847, ~: 1504904)\nLogs:\n  Bound result 24999000000000000000003722\n  Bound result 24999007000000000000000001\n  Bound result 24999000000000003575104395\n  Bound result 5653\n  Bound result 1055\n\n[PASS] test_betRefusedAtCloseAfterEarlySettlementAndAfterExpiry() (gas: 1861913)\n[PASS] test_claimEdgeCases() (gas: 1351819)\n[PASS] test_createRefusesWrongFieldsAndUnsupportedToken() (gas: 1491845)\n[PASS] test_feeRoundsDownOnOddTinyLosingSide() (gas: 1084459)\n[PASS] test_feeZeroWhenLosingSideBelowTwentyWei() (gas: 1936441)\n[PASS] test_oracleBothMarketsVoidRefundExactly() (gas: 5116409)\n[PASS] test_oracleIMD_AWins() (gas: 2795219)\n[PASS] test_oracleIMD_BWins() (gas: 2775320)\n[PASS] test_oracleSHIFT_AWins() (gas: 2513696)\n[PASS] test_oracleSHIFT_BWins() (gas: 2513857)\n[PASS] test_ownerCannotTakeFundsThroughAnyRoute() (gas: 1544558)\n[PASS] test_pauseBlocksCreateAndBetOnly() (gas: 1520465)\n[PASS] test_settleWithSignatureForOtherMarketOrOutcomeRefused() (gas: 1293866)\n[PASS] test_signedButOutOfRangeTicketsRefused() (gas: 3368556)\n[PASS] test_ticketExpiryBoundaries() (gas: 700698)\n[PASS] test_ticketForOneSideOrAmountCannotBeUsedForAnother() (gas: 943543)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 366.65ms (305.64ms CPU time)\n\nRan 33 tests for test/BetPoolSafety.t.sol:BetPoolSafetyTest\n[PASS] test_IMDAmountsOutsideLimitsRejected() (gas: 251747)\n[PASS] test_SHIFTAmountsOutsideLimitsRejected() (gas: 252008)\n[PASS] test_bettingExclusiveCloseBoundary() (gas: 591966)\n[PASS] test_decisiveSettlementWaitsForCloseButVoidClosesBettingImmediately() (gas: 1898208)\n[PASS] test_duplicatePauseAndUnpauseRejectedAndConfigurationWorksWhilePaused() (gas: 222661)\n[PASS] test_factoryDeploymentWithoutAnyExternalContractsOrPrivileges() (gas: 2219063)\n[PASS] test_failedClaimPreservesEntitlementAndDoesNotBlockOthers() (gas: 1284617)\n[PASS] test_failedFeeTransferRollsBackSettlementAndCanExpireLater() (gas: 1328019)\n[PASS] test_heldTicketCannotBeSubmittedAheadOfTheOutcomeThatDecidesIt() (gas: 1393392)\n[PASS] test_inexactTransfersRevertAndRollBackEverything() (gas: 1311486)\n[PASS] test_insufficientAllowanceAndBalanceRollBack() (gas: 779584)\n[PASS] test_invalidOutcomeEvenIfSignedRejected() (gas: 131325)\n[PASS] test_invalidSideAndWeightRejectedEvenIfSignerApproves() (gas: 315574)\n[PASS] test_limitsCanReachButNeverExceedEitherHardCap() (gas: 276560)\n[PASS] test_minimumCanBeOneWeiButZeroShareTicketCannotCreateUndefinedPayout() (gas: 1087450)\n[PASS] test_newLimitsApplyToPendingTicketsAndExistingMarketsOnlyProspectively() (gas: 1120017)\n[PASS] test_noClaimsBeforeSettlementAndNoBetsOrDoubleSettleAfterwards() (gas: 719698)\n[PASS] test_onlyOwnerMayConfigureOrPause() (gas: 123401)\n[PASS] test_onlyTwoTokensAccepted() (gas: 112209)\n[PASS] test_ownerHasNoCustodyRedirectionOrOwnershipTransferFunctions() (gas: 741151)\n[PASS] test_pauseBlocksOnlyNewMarketsAndBets() (gas: 2280239)\n[PASS] test_reentrantBetCallbackIsBlocked() (gas: 681869)\n[PASS] test_reentrantClaimAndClaimManyCallbacksAreBlocked() (gas: 1413396)\n[PASS] test_reentrantSettlementFeeCallbackIsBlocked() (gas: 1153786)\n[PASS] test_runtimeSizeAndNoForbiddenOpcodes() (gas: 4513226)\n[PASS] test_safeERC20AcceptsNoReturnData() (gas: 1108338)\n[PASS] test_settleAfterExpireRejected() (gas: 207799)\n[PASS] test_settlementRefusedFromRefundDelayOnSoExpiryCannotBeRacedBySettle() (gas: 1479697)\n[PASS] test_signerCannotBeSetToZero() (gas: 43305)\n[PASS] test_tokenWithoutCodeCannotAcceptBet() (gas: 169825)\n[PASS] test_transferFromFailureDoesNotConsumeNonceOrRecordBet() (gas: 1103975)\n[PASS] test_twentyWeiMinimumWorksAtSmallestWeight() (gas: 1102041)\n[PASS] test_unknownMarketsRejectAllMutationsAndViewsReturnZero() (gas: 196863)\nSuite result: ok. 33 passed; 0 failed; 0 skipped; finished in 366.67ms (45.71ms CPU time)\n\nRan 21 tests for test/BetPoolFlows.t.sol:BetPoolFlowsTest\n[PASS] test_claimManyDuplicateRollsBackEntireBatch() (gas: 813741)\n[PASS] test_claimManyMixesTokensAndLosingClaims() (gas: 2303369)\n[PASS] test_claimManyUnknownOrUnsettledRollsBack() (gas: 904556)\n[PASS] test_constructorDefaultsIncludePublishedNightshiftSigner() (gas: 300260)\n[PASS] test_emptyMarketCanSettleAllOutcomes() (gas: 799560)\n[PASS] test_exactPayoutsAndFeeForSimpleWeights() (gas: 1528530)\n[PASS] test_expireEmptyMarket() (gas: 210270)\n[PASS] test_expiryAtBoundaryRefundsAndFinalizes() (gas: 1477724)\n[PASS] test_fullWeightedFlowIMD_AWins() (gas: 3671777)\n[PASS] test_fullWeightedFlowIMD_BWins() (gas: 3672976)\n[PASS] test_fullWeightedFlowSHIFT_AWins() (gas: 3672556)\n[PASS] test_fullWeightedFlowSHIFT_BWins() (gas: 3673028)\n[PASS] test_losersAndNonBettorsReceiveNothingAndCannotClaimTwice() (gas: 1258445)\n[PASS] test_marketPotsAreIndependentAndDonationsDoNotChangePayouts() (gas: 1723494)\n[PASS] test_oddLosingAmountRoundsFeeDownAndOnlyBonusCreatesDust() (gas: 1469251)\n[PASS] test_oneSidedBRefundedEvenWhenAIsDeclaredWinner() (gas: 677969)\n[PASS] test_oneSidedLosingSideRefunded() (gas: 1102665)\n[PASS] test_oneSidedVoidRefunded() (gas: 1097633)\n[PASS] test_oneSidedWinningSideRefunded() (gas: 1102678)\n[PASS] test_voidRefundsBothSidesIMD() (gas: 1421759)\n[PASS] test_voidRefundsBothSidesSHIFT() (gas: 1421818)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 370.90ms (61.54ms CPU time)\n\nRan 2 tests for test/BetPoolFuzz.t.sol:BetPoolFuzzTest\n[PASS] testFuzz_limitsWithinHardCapsNeverOverflowShares(bool,uint128,uint16,bool) (runs: 512, μ: 1117101, ~: 1117055)\nLogs:\n  Bound result 3876965170673591263\n  Bound result 501\n\n[PASS] testFuzz_payoutsFollowWeightedSharesAndConserveBothTokens(uint128[8],uint16[8],bool,uint8,bool,bool) (runs: 512, μ: 5492405, ~: 6621175)\nLogs:\n  Bound result 2\n  Bound result 24999000000000000001153309\n  Bound result 502\n  Bound result 1000000000000000000003\n  Bound result 9786\n  Bound result 24999000000000000000000432\n  Bound result 9645\n  Bound result 449524761240621333865557\n  Bound result 6456\n  Bound result 24999000000057684753104739\n  Bound result 9650\n  Bound result 24999000000190002323961067\n  Bound result 9954\n  Bound result 6532636564684530779393193\n  Bound result 501\n  Bound result 2327426706077906781068\n  Bound result 8526\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 371.76ms (477.58ms CPU time)\n\nRan 1 test for test/BetPoolAttackInvariant.t.sol:BetPoolAttackInvariantTest\n[PASS]\nBetPoolAttackInvariantTest invariants:\n[PASS] invariant_finishedMarketsStayFinalAndStrangersGetNothing\n[PASS] invariant_poolHoldsWhatItOwesAndNeverOverpays\n BetPoolAttackInvariantTest invariants (runs: 64, calls: 4096, reverts: 0)\n\n╭----------------------+---------------------+-------+---------+----------╮\n| Contract             | Selector            | Calls | Reverts | Discards |\n+=========================================================================+\n| BetPoolAttackHandler | advance             | 269   | 0       | 0        |\n|----------------------+---------------------+-------+---------+----------|\n| BetPoolAttackHandler | betAfterClose       | 265   | 0       | 0        |\n|----------------------+---------------------+-------+---------+----------|\n| BetPoolAttackHandler | claim               | 259   | 0       | 0        |\n|----------------------+---------------------+-------+---------+----------|\n| BetPoolAttackHandler | create              | 267   | 0       | 0        |\n|----------------------+---------------------+-------+---------+----------|\n| BetPoolAttackHandler | expire              | 240   | 0       | 0        |\n|----------------------+---------------------+-------+---------+----------|\n| BetPoolAttackHandler | forgedSettle        | 243   | 0       | 0        |\n|----------------------+---------------------+-------+---------+----------|\n| BetPoolAttackHandler | forgedTicket        | 273   | 0       | 0        |\n|----------------------+---------------------+-------+---------+----------|\n| BetPoolAttackHandler | place               | 476   | 0       | 0        |\n|----------------------+---------------------+-------+---------+----------|\n| BetPoolAttackHandler | replayLastTicket    | 254   | 0       | 0        |\n|----------------------+---------------------+-------+---------+----------|\n| BetPoolAttackHandler | resettle            | 270   | 0       | 0        |\n|----------------------+---------------------+-------+---------+----------|\n| BetPoolAttackHandler | settle              | 248   | 0       | 0        |\n|----------------------+---------------------+-------+---------+----------|\n| BetPoolAttackHandler | settleOutsideWindow | 267   | 0       | 0        |\n|----------------------+---------------------+-------+---------+----------|\n| BetPoolAttackHandler | stealTicket         | 252   | 0       | 0        |\n|----------------------+---------------------+-------+---------+----------|\n| BetPoolAttackHandler | strangerAdmin       | 250   | 0       | 0        |\n|----------------------+---------------------+-------+---------+----------|\n| BetPoolAttackHandler | strangerClaims      | 263   | 0       | 0        |\n╰----------------------+---------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 499900000000000000067\n  Bound result 3769\n  Bound result 1\n  Bound result 100000000000000001\n  Bound result 24999000000000000000011353\n  Bound result 9634\n  Bound result 0\n  Bound result 3502\n  Bound result 0\n  Bound result 2767\n  Bound result 407423\n  Bound result 4618\n  Bound result 601671\n  Bound result 12326\n  Bound result 365591\n  Bound result 28460\n  Bound result 499900000000000065281\n  Bound result 2539\n  Bound result 1854\n  Bound result 0\n  Bound result 0\n  Bound result 88673\n  Bound result 19859906039547231\n  Bound result 18504\n  Bound result 9551155200805498246820961\n  Bound result 1374\n  Bound result 1\n  Bound result 499900009971936949664\n  Bound result 7129\n  Bound result 0\n  Bound result 1\n  Bound result 330472932821646943677\n  Bound result 9311\n  Bound result 1\n  Bound result 2\n  Bound result 24999000000000000000007867\n  Bound result 9621\n  Bound result 18337790640760235651177181\n  Bound result 2937\n  Bound result 2\n  Bound result 24999000000000000000014475\n  Bound result 8364\n  Bound result 1201514833480919536930869\n  Bound result 13282\n  Bound result 24999002000000000000000001\n  Bound result 4000000000000000000000\n  Bound result 9540\n  Bound result 0\n  Bound result 2\n  Bound result 499900000000000000501\n  Bound result 10000\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 5.48s (5.48s CPU time)\n\nRan 1 test for test/BetPoolInvariant.t.sol:BetPoolInvariantTest\n[PASS]\nBetPoolInvariantTest invariants:\n[PASS] invariant_actualBalancesCoverAllMarketsAndConserveEveryWei\n[PASS] invariant_authorityAndNoncesRemainCorrect\n[PASS] invariant_marketStateAndEveryClaimMatchIndependentModel\n BetPoolInvariantTest invariants (runs: 128, calls: 12288, reverts: 0)\n\n╭----------------+--------------+-------+---------+----------╮\n| Contract       | Selector     | Calls | Reverts | Discards |\n+============================================================+\n| BetPoolHandler | adjustLimits | 1107  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | advance      | 1165  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | claim        | 1106  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | claimMany    | 1069  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | create       | 1088  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | donate       | 1181  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | expire       | 1061  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | place        | 1193  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | rotateSigner | 1133  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | settle       | 1106  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | togglePause  | 1079  | 0       | 0        |\n╰----------------+--------------+-------+---------+----------╯\n\nLogs:\n  Bound result 10000000000000000000\n  Bound result 10000\n  Bound result 10000000000000000000\n  Bound result 5000\n  Bound result 10000000000000000000\n  Bound result 4000\n  Bound result 10000000000000000000\n  Bound result 500\n  Bound result 1000000000000000000000000\n  Bound result 10000\n  Bound result 1000000000000000000000000\n  Bound result 5000\n  Bound result 1000000000000000000000000\n  Bound result 4000\n  Bound result 1000000000000000000000000\n  Bound result 500\n  Bound result 10000000000000000000\n  Bound result 10000\n  Bound result 10000000000000000000\n  Bound result 5000\n  Bound result 10000000000000000000\n  Bound result 4000\n  Bound result 10000000000000000000\n  Bound result 500\n  Bound result 1000000000000000000000000\n  Bound result 10000\n  Bound result 1000000000000000000000000\n  Bound result 5000\n  Bound result 1000000000000000000000000\n  Bound result 4000\n  Bound result 1000000000000000000000000\n  Bound result 500\n  Bound result 65279\n  Bound result 499999999999999999999943014\n  Bound result 31182839450617283945\n  Bound result 2849\n  Bound result 6571\n  Bound result 157470248635966934019\n  Bound result 9842529751364033073318\n  Bound result 5000\n  Bound result 3963891462\n  Bound result 0\n  Bound result 12815660728589021578977957\n  Bound result 488418906271410978421022044\n  Bound result 6722540745621196690441\n  Bound result 9832377763136409933631\n  Bound result 1000000000000000000000\n  Bound result 499999000000000000000000029\n  Bound result 646\n  Bound result 2510\n  Bound result 99999999999999999\n  Bound result 978\n  Bound result 499998000000000000000004944\n  Bound result 5142\n  Bound result 705\n  Bound result 6224\n  Bound result 99999999999999051\n  Bound result 9521\n  Bound result 530\n  Bound result 878340192\n  Bound result 603546\n  Bound result 394892196923676441702320755\n  Bound result 417807781869536290929843294\n  Bound result 1785\n  Bound result 9000\n  Bound result 405259479042112148068\n  Bound result 5327392583068602357338\n  Bound result 1\n  Bound result 921\n  Bound result 12545\n  Bound result 9999999999999999997079\n  Bound result 1\n  Bound result 412480529025477286095411527\n  Bound result 1149\n  Bound result 43109933464208042242132\n  Bound result 69206\n  Bound result 405692699322060444063776314\n  Bound result 471536503389698387335956274\n  Bound result 253827\n  Bound result 1643\n  Bound result 1191\n  Bound result 560740242366515386407\n  Bound result 39\n  Bound result 0\n  Bound result 51525089824\n  Bound result 505667\n  Bound result 2\n  Bound result 0\n  Bound result 7200\n  Bound result 15146\n  Bound result 499999999999999999999984870\n  Bound result 499999999999999999999974725\n  Bound result 9518\n  Bound result 499999999999999999999969735\n  Bound result 1475\n  Bound result 9009\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 52.23s (52.22s CPU time)\n\nRan 8 test suites in 52.24s (59.56s CPU time): 106 tests passed, 0 failed, 0 skipped (106 total tests)\n","passed":true},{"durationMs":103,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"BetPool.bet(bytes32,uint8,uint256,uint16,uint64,bytes32,bytes)\",\"BetPool.claim(bytes32)\",\"BetPool.claimMany(bytes32[])\",\"BetPool.createMarket(bytes32,address,uint64,bytes32,bytes)\",\"BetPool.expire(bytes32)\",\"BetPool.pause()\",\"BetPool.setLimits(address,uint256,uint256)\",\"BetPool.setSigner(address)\",\"BetPool.settle(bytes32,uint8,uint256,bytes)\",\"BetPool.unpause()\"],\"files\":{\".gitignore\":4,\"LICENSE\":21,\"README.md\":110,\"docs/DEPENDENCIES.md\":12,\"docs/REVIEW.md\":60,\"docs/eip712-types.json\":28,\"foundry.toml\":22,\"remappings.txt\":2,\"src/BetPool.sol\":367,\"test/BetPoolAttackInvariant.t.sol\":82,\"test/BetPoolEvents.t.sol\":69,\"test/BetPoolFlows.t.sol\":338,\"test/BetPoolFuzz.t.sol\":115,\"test/BetPoolInvariant.t.sol\":125,\"test/BetPoolOracle.t.sol\":512,\"test/BetPoolSafety.t.sol\":579,\"test/BetPoolSignatures.t.sol\":339,\"test/helpers/BetPoolAttackHandler.sol\":424,\"test/helpers/BetPoolHandler.sol\":298,\"test/helpers/BetPoolTestBase.sol\":144,\"test/helpers/MockToken.sol\":76},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"44e3a8f49ffa5a7d1a50bc32994423dbc6e25ee8df193e44dd85960e07626a36","verifiedTreeHash":"fdea24f0b3dbde153e858d64a62925a1ea543cbd","verifierVersion":"0.1.0+a6ee6548"},{"checks":[{"durationMs":23567,"exitCode":0,"name":"build","output":"Compiling 48 files with Solc 0.8.26\nSolc 0.8.26 finished in 23.50s\nCompiler run successful!\n","passed":true},{"durationMs":28285,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 3 tests for test/BetPoolEvents.t.sol:BetPoolEventsTest\n[PASS] test_adminAndExpiryEvents() (gas: 327742)\n[PASS] test_marketBetSettlementAndClaimEvents() (gas: 1064586)\n[PASS] test_refundableSettlementEmitsZeroFee() (gas: 576250)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 2.35ms (2.35ms CPU time)\n\nRan 27 tests for test/BetPoolSignatures.t.sol:BetPoolSignaturesTest\n[PASS] testFuzz_createRejectsWrongSignerContractChain(uint8) (runs: 512, μ: 56555, ~: 56619)\nLogs:\n  Bound result 1\n\n[PASS] testFuzz_createSignatureBindsEveryField(uint8) (runs: 512, μ: 56393, ~: 56394)\nLogs:\n  Bound result 1\n\n[PASS] testFuzz_malformedSignatureLengthsRejected(uint8) (runs: 512, μ: 59780, ~: 59615)\nLogs:\n  Bound result 36\n\n[PASS] testFuzz_outcomeSignatureBindsEveryField(uint8) (runs: 512, μ: 167334, ~: 167449)\nLogs:\n  Bound result 1\n\n[PASS] testFuzz_settleRejectsWrongSignerContractChain(uint8) (runs: 512, μ: 77864, ~: 77925)\nLogs:\n  Bound result 2\n\n[PASS] testFuzz_ticketSignatureBindsEveryField(uint8) (runs: 512, μ: 105106, ~: 84251)\nLogs:\n  Bound result 0\n\n[PASS] test_badVAndZeroRecoveryRejected() (gas: 112108)\n[PASS] test_createSignatureCannotBeReplayedOnNewDeployment() (gas: 82637)\n[PASS] test_creationAtNowPastAndTooFarRejected() (gas: 124513)\n[PASS] test_creationAtSevenDaysAndPermissionlessSubmission() (gas: 117374)\n[PASS] test_domainAndTypeHashesMatchExactSpecification() (gas: 41251)\n[PASS] test_domainChangesAfterChainIdChange() (gas: 322841)\n[PASS] test_duplicateMarketIdCannotBeReusedAfterSettlement() (gas: 169863)\n[PASS] test_expiredSettlementRejected() (gas: 67504)\n[PASS] test_highSMalleatedSignatureRejected() (gas: 288305)\n[PASS] test_nonceIsGlobalAcrossMarketsWalletsAndSignerRotations() (gas: 410198)\n[PASS] test_ownerCannotSettleWithoutSigner() (gas: 86041)\n[PASS] test_personalSignSignatureRejected() (gas: 65513)\n[PASS] test_rotationRevokesAllOldSignatureTypesAndNewSignerWorks() (gas: 610660)\n[PASS] test_settlementDeadlineInclusiveAndPermissionless() (gas: 118455)\n[PASS] test_ticketExpiredRejectedEvenWithValidSignature() (gas: 56353)\n[PASS] test_ticketExpiryAtCurrentTimeAndMaximumAccepted() (gas: 385184)\n[PASS] test_ticketNonceCannotBeReusedEvenWithDifferentSignedFields() (gas: 347751)\n[PASS] test_ticketTooFarExpiryRejectedEvenWithValidSignature() (gas: 56817)\n[PASS] test_ticketWrongChain() (gas: 65369)\n[PASS] test_ticketWrongContract() (gas: 65144)\n[PASS] test_ticketWrongSigner() (gas: 65085)\nSuite result: ok. 27 passed; 0 failed; 0 skipped; finished in 77.86ms (272.90ms CPU time)\n\nRan 21 tests for test/BetPoolFlows.t.sol:BetPoolFlowsTest\n[PASS] test_claimManyDuplicateRollsBackEntireBatch() (gas: 813741)\n[PASS] test_claimManyMixesTokensAndLosingClaims() (gas: 2303369)\n[PASS] test_claimManyUnknownOrUnsettledRollsBack() (gas: 904556)\n[PASS] test_constructorDefaultsIncludePublishedNightshiftSigner() (gas: 300260)\n[PASS] test_emptyMarketCanSettleAllOutcomes() (gas: 799560)\n[PASS] test_exactPayoutsAndFeeForSimpleWeights() (gas: 1528530)\n[PASS] test_expireEmptyMarket() (gas: 210270)\n[PASS] test_expiryAtBoundaryRefundsAndFinalizes() (gas: 1477724)\n[PASS] test_fullWeightedFlowIMD_AWins() (gas: 3671777)\n[PASS] test_fullWeightedFlowIMD_BWins() (gas: 3672976)\n[PASS] test_fullWeightedFlowSHIFT_AWins() (gas: 3672556)\n[PASS] test_fullWeightedFlowSHIFT_BWins() (gas: 3673028)\n[PASS] test_losersAndNonBettorsReceiveNothingAndCannotClaimTwice() (gas: 1258445)\n[PASS] test_marketPotsAreIndependentAndDonationsDoNotChangePayouts() (gas: 1723494)\n[PASS] test_oddLosingAmountRoundsFeeDownAndOnlyBonusCreatesDust() (gas: 1469251)\n[PASS] test_oneSidedBRefundedEvenWhenAIsDeclaredWinner() (gas: 677969)\n[PASS] test_oneSidedLosingSideRefunded() (gas: 1102665)\n[PASS] test_oneSidedVoidRefunded() (gas: 1097633)\n[PASS] test_oneSidedWinningSideRefunded() (gas: 1102678)\n[PASS] test_voidRefundsBothSidesIMD() (gas: 1421759)\n[PASS] test_voidRefundsBothSidesSHIFT() (gas: 1421818)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 205.43ms (33.38ms CPU time)\n\nRan 33 tests for test/BetPoolSafety.t.sol:BetPoolSafetyTest\n[PASS] test_IMDAmountsOutsideLimitsRejected() (gas: 251747)\n[PASS] test_SHIFTAmountsOutsideLimitsRejected() (gas: 252008)\n[PASS] test_bettingExclusiveCloseBoundary() (gas: 591966)\n[PASS] test_decisiveSettlementWaitsForCloseButVoidClosesBettingImmediately() (gas: 1898208)\n[PASS] test_duplicatePauseAndUnpauseRejectedAndConfigurationWorksWhilePaused() (gas: 222661)\n[PASS] test_factoryDeploymentWithoutAnyExternalContractsOrPrivileges() (gas: 2219063)\n[PASS] test_failedClaimPreservesEntitlementAndDoesNotBlockOthers() (gas: 1284617)\n[PASS] test_failedFeeTransferRollsBackSettlementAndCanExpireLater() (gas: 1328019)\n[PASS] test_heldTicketCannotBeSubmittedAheadOfTheOutcomeThatDecidesIt() (gas: 1393392)\n[PASS] test_inexactTransfersRevertAndRollBackEverything() (gas: 1311486)\n[PASS] test_insufficientAllowanceAndBalanceRollBack() (gas: 779584)\n[PASS] test_invalidOutcomeEvenIfSignedRejected() (gas: 131325)\n[PASS] test_invalidSideAndWeightRejectedEvenIfSignerApproves() (gas: 315574)\n[PASS] test_limitsCanReachButNeverExceedEitherHardCap() (gas: 276560)\n[PASS] test_minimumCanBeOneWeiButZeroShareTicketCannotCreateUndefinedPayout() (gas: 1087450)\n[PASS] test_newLimitsApplyToPendingTicketsAndExistingMarketsOnlyProspectively() (gas: 1120017)\n[PASS] test_noClaimsBeforeSettlementAndNoBetsOrDoubleSettleAfterwards() (gas: 719698)\n[PASS] test_onlyOwnerMayConfigureOrPause() (gas: 123401)\n[PASS] test_onlyTwoTokensAccepted() (gas: 112209)\n[PASS] test_ownerHasNoCustodyRedirectionOrOwnershipTransferFunctions() (gas: 741151)\n[PASS] test_pauseBlocksOnlyNewMarketsAndBets() (gas: 2280239)\n[PASS] test_reentrantBetCallbackIsBlocked() (gas: 681869)\n[PASS] test_reentrantClaimAndClaimManyCallbacksAreBlocked() (gas: 1413396)\n[PASS] test_reentrantSettlementFeeCallbackIsBlocked() (gas: 1153786)\n[PASS] test_runtimeSizeAndNoForbiddenOpcodes() (gas: 4513226)\n[PASS] test_safeERC20AcceptsNoReturnData() (gas: 1108338)\n[PASS] test_settleAfterExpireRejected() (gas: 207799)\n[PASS] test_settlementRefusedFromRefundDelayOnSoExpiryCannotBeRacedBySettle() (gas: 1479697)\n[PASS] test_signerCannotBeSetToZero() (gas: 43305)\n[PASS] test_tokenWithoutCodeCannotAcceptBet() (gas: 169825)\n[PASS] test_transferFromFailureDoesNotConsumeNonceOrRecordBet() (gas: 1103975)\n[PASS] test_twentyWeiMinimumWorksAtSmallestWeight() (gas: 1102041)\n[PASS] test_unknownMarketsRejectAllMutationsAndViewsReturnZero() (gas: 196863)\nSuite result: ok. 33 passed; 0 failed; 0 skipped; finished in 210.46ms (39.04ms CPU time)\n\nRan 2 tests for test/BetPoolFuzz.t.sol:BetPoolFuzzTest\n[PASS] testFuzz_limitsWithinHardCapsNeverOverflowShares(bool,uint128,uint16,bool) (runs: 512, μ: 1117095, ~: 1117055)\nLogs:\n  Bound result 2345810831772884312383\n  Bound result 9560\n\n[PASS] testFuzz_payoutsFollowWeightedSharesAndConserveBothTokens(uint128[8],uint16[8],bool,uint8,bool,bool) (runs: 512, μ: 5491702, ~: 6621096)\nLogs:\n  Bound result 0\n  Bound result 100000000000000002\n  Bound result 1396\n  Bound result 190201705729447738196\n  Bound result 8647\n  Bound result 499900000000002824400\n  Bound result 9507\n  Bound result 246713227179482250541\n  Bound result 9579\n  Bound result 499900000000000023822\n  Bound result 9696\n  Bound result 170931220433323550814\n  Bound result 2535\n  Bound result 189068544364349365541\n  Bound result 4072\n  Bound result 308528587173889502187\n  Bound result 5431\n  Bound result 499900000000000000013\n  Bound result 8526\n  Bound result 2794522138484845334\n  Bound result 6327\n  Bound result 308662938692120559874\n  Bound result 501\n  Bound result 382474486424091973676\n  Bound result 9514\n  Bound result 499953104653800217341\n  Bound result 500\n  Bound result 60613194625426682555\n  Bound result 500\n  Bound result 499900000000202913227\n  Bound result 501\n  Bound result 192911348331018626531\n  Bound result 9514\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 210.52ms (286.74ms CPU time)\n\nRan 1 test for test/BetPoolInvariant.t.sol:BetPoolInvariantTest\n[PASS]\nBetPoolInvariantTest invariants:\n[PASS] invariant_actualBalancesCoverAllMarketsAndConserveEveryWei\n[PASS] invariant_authorityAndNoncesRemainCorrect\n[PASS] invariant_marketStateAndEveryClaimMatchIndependentModel\n BetPoolInvariantTest invariants (runs: 128, calls: 12288, reverts: 0)\n\n╭----------------+--------------+-------+---------+----------╮\n| Contract       | Selector     | Calls | Reverts | Discards |\n+============================================================+\n| BetPoolHandler | adjustLimits | 1107  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | advance      | 1165  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | claim        | 1106  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | claimMany    | 1069  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | create       | 1088  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | donate       | 1181  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | expire       | 1061  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | place        | 1193  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | rotateSigner | 1133  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | settle       | 1106  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | togglePause  | 1079  | 0       | 0        |\n╰----------------+--------------+-------+---------+----------╯\n\nLogs:\n  Bound result 10000000000000000000\n  Bound result 10000\n  Bound result 10000000000000000000\n  Bound result 5000\n  Bound result 10000000000000000000\n  Bound result 4000\n  Bound result 10000000000000000000\n  Bound result 500\n  Bound result 1000000000000000000000000\n  Bound result 10000\n  Bound result 1000000000000000000000000\n  Bound result 5000\n  Bound result 1000000000000000000000000\n  Bound result 4000\n  Bound result 1000000000000000000000000\n  Bound result 500\n  Bound result 10000000000000000000\n  Bound result 10000\n  Bound result 10000000000000000000\n  Bound result 5000\n  Bound result 10000000000000000000\n  Bound result 4000\n  Bound result 10000000000000000000\n  Bound result 500\n  Bound result 1000000000000000000000000\n  Bound result 10000\n  Bound result 1000000000000000000000000\n  Bound result 5000\n  Bound result 1000000000000000000000000\n  Bound result 4000\n  Bound result 1000000000000000000000000\n  Bound result 500\n  Bound result 65279\n  Bound result 499999999999999999999943014\n  Bound result 499999999999999999999877749\n  Bound result 2849\n  Bound result 6569\n  Bound result 1\n  Bound result 9999999999999999999987\n  Bound result 9999999999999999999996\n  Bound result 4\n  Bound result 3963891462\n  Bound result 777\n  Bound result 8192\n  Bound result 2658\n  Bound result 5072\n  Bound result 86400\n  Bound result 499999999999999999999913665\n  Bound result 1\n  Bound result 9642\n  Bound result 9999999999999999990601\n  Bound result 1124\n  Bound result 152\n  Bound result 15311\n  Bound result 499999999999999999999828915\n  Bound result 502\n  Bound result 398\n  Bound result 500000000000000000000\n  Bound result 9596\n  Bound result 4917\n  Bound result 602302\n  Bound result 394892196923676441702320755\n  Bound result 417807781869536290929843294\n  Bound result 2\n  Bound result 1799\n  Bound result 355672\n  Bound result 405259479042112148068\n  Bound result 5327392583068602357338\n  Bound result 4922133104026490214272\n  Bound result 2671\n  Bound result 921\n  Bound result 12627\n  Bound result 9999999999999999997027\n  Bound result 10\n  Bound result 603546\n  Bound result 405692699322060444063776314\n  Bound result 471536503389698387335956274\n  Bound result 253827\n  Bound result 90\n  Bound result 2514000706\n  Bound result 499999999999999997486003391\n  Bound result 244\n  Bound result 51525089824\n  Bound result 505667\n  Bound result 499999999999999994972005031\n  Bound result 10000\n  Bound result 2\n  Bound result 0\n  Bound result 7674128\n  Bound result 15188\n  Bound result 10000000000000000000\n  Bound result 8827\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 28.20s (28.20s CPU time)\n\nRan 6 test suites in 28.21s (28.91s CPU time): 87 tests passed, 0 failed, 0 skipped (87 total tests)\n","passed":true},{"durationMs":43,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"BetPool.bet(bytes32,uint8,uint256,uint16,uint64,bytes32,bytes)\",\"BetPool.claim(bytes32)\",\"BetPool.claimMany(bytes32[])\",\"BetPool.createMarket(bytes32,address,uint64,bytes32,bytes)\",\"BetPool.expire(bytes32)\",\"BetPool.pause()\",\"BetPool.setLimits(address,uint256,uint256)\",\"BetPool.setSigner(address)\",\"BetPool.settle(bytes32,uint8,uint256,bytes)\",\"BetPool.unpause()\"],\"files\":{\".gitignore\":4,\"LICENSE\":21,\"README.md\":110,\"docs/DEPENDENCIES.md\":12,\"docs/REVIEW.md\":60,\"docs/eip712-types.json\":28,\"foundry.toml\":22,\"remappings.txt\":2,\"src/BetPool.sol\":367,\"test/BetPoolEvents.t.sol\":69,\"test/BetPoolFlows.t.sol\":338,\"test/BetPoolFuzz.t.sol\":115,\"test/BetPoolInvariant.t.sol\":125,\"test/BetPoolSafety.t.sol\":579,\"test/BetPoolSignatures.t.sol\":339,\"test/helpers/BetPoolHandler.sol\":298,\"test/helpers/BetPoolTestBase.sol\":144,\"test/helpers/MockToken.sol\":76},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":1419,"exitCode":0,"name":"slither","output":"[high/medium] reentrancy-balance at src/BetPool.sol:198: Reentrancy in BetPool.bet(bytes32,uint8,uint256,uint16,uint64,bytes32,bytes) (src/BetPool.sol#198-235):\n[medium/medium] unused-return at src/BetPool.sol:349: BetPool._verify(bytes32,bytes) (src/BetPool.sol#349-353) ignores return value by (recovered,error,None) = ECDSA.tryRecover(_hashTypedDataV4(structHash),sig) (src/BetPool.sol#351)\n[low/medium] timestamp at src/BetPool.sol:173: BetPool.createMarket(bytes32,address,uint64,bytes32,bytes) (src/BetPool.sol#173-189) uses timestamp for comparisons\n[low/medium] timestamp at src/BetPool.sol:270: BetPool.expire(bytes32) (src/BetPool.sol#270-278) uses timestamp for comparisons\n[low/medium] timestamp at src/BetPool.sol:198: BetPool.bet(bytes32,uint8,uint256,uint16,uint64,bytes32,bytes) (src/BetPool.sol#198-235) uses timestamp for comparisons\n[low/medium] timestamp at src/BetPool.sol:245: BetPool.settle(bytes32,uint8,uint256,bytes) (src/BetPool.sol#245-266) uses timestamp for comparisons","passed":true},{"durationMs":306,"exitCode":0,"name":"aderyn","output":"[high] reentrancy-state-change at src/BetPool.sol:228: Reentrancy: State change after external call\n[low] centralization-risk at src/BetPool.sol:145: Centralization Risk (4 places)\n[low] costly-loop at src/BetPool.sol:290: Costly operations inside loop\n[low] large-numeric-literal at src/BetPool.sol:32: Large Numeric Literal (5 places)\n[low] literal-instead-of-constant at src/BetPool.sol:261: Literal Instead of Constant (2 places)\n[low] require-revert-in-loop at src/BetPool.sol:290: Loop Contains `require`/`revert`\n[low] unchecked-return at src/BetPool.sol:179: Unchecked Return (2 places)\n[low] uninitialized-local-variable at src/BetPool.sol:290: Uninitialized Local Variable","passed":true},{"durationMs":4109,"exitCode":0,"name":"proof 765d56417cea","output":"Compiling 43 files with Solc 0.8.26\nSolc 0.8.26 finished in 3.66s\nCompiler run successful!\n\nRan 1 test for test/imd-proof-fd65f453/Proof_765d56417cea.t.sol:HeldTicketRaceTest\n[PASS] test_heldTicketSubmittedAheadOfOutcomeEarnsNoShareOfLosers() (gas: 1061932)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.55ms (831.46µs CPU time)\n\nRan 1 test suite in 2.50ms (1.55ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)\n","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"4a50f6ff9e5b2a469a6e9b9e62980b09fac2fe5422a59e5fb2fa5de004ef63ae","verifiedTreeHash":"0f1c8da808919b63f158cdfb4cb96b148b5f78fb","verifierVersion":"0.1.0+a6ee6548"},{"checks":[{"durationMs":27816,"exitCode":0,"name":"build","output":"Compiling 48 files with Solc 0.8.26\nSolc 0.8.26 finished in 27.74s\nCompiler run successful!\n","passed":true},{"durationMs":30784,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 3 tests for test/BetPoolEvents.t.sol:BetPoolEventsTest\n[PASS] test_adminAndExpiryEvents() (gas: 327742)\n[PASS] test_marketBetSettlementAndClaimEvents() (gas: 1064586)\n[PASS] test_refundableSettlementEmitsZeroFee() (gas: 576250)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 4.73ms (7.57ms CPU time)\n\nRan 27 tests for test/BetPoolSignatures.t.sol:BetPoolSignaturesTest\n[PASS] testFuzz_createRejectsWrongSignerContractChain(uint8) (runs: 512, μ: 56555, ~: 56619)\nLogs:\n  Bound result 2\n\n[PASS] testFuzz_createSignatureBindsEveryField(uint8) (runs: 512, μ: 56393, ~: 56394)\nLogs:\n  Bound result 0\n\n[PASS] testFuzz_malformedSignatureLengthsRejected(uint8) (runs: 512, μ: 59780, ~: 59615)\nLogs:\n  Bound result 36\n\n[PASS] testFuzz_outcomeSignatureBindsEveryField(uint8) (runs: 512, μ: 167334, ~: 167449)\nLogs:\n  Bound result 2\n\n[PASS] testFuzz_settleRejectsWrongSignerContractChain(uint8) (runs: 512, μ: 77864, ~: 77925)\nLogs:\n  Bound result 1\n\n[PASS] testFuzz_ticketSignatureBindsEveryField(uint8) (runs: 512, μ: 105106, ~: 84251)\nLogs:\n  Bound result 0\n\n[PASS] test_badVAndZeroRecoveryRejected() (gas: 112108)\n[PASS] test_createSignatureCannotBeReplayedOnNewDeployment() (gas: 82637)\n[PASS] test_creationAtNowPastAndTooFarRejected() (gas: 124513)\n[PASS] test_creationAtSevenDaysAndPermissionlessSubmission() (gas: 117374)\n[PASS] test_domainAndTypeHashesMatchExactSpecification() (gas: 41251)\n[PASS] test_domainChangesAfterChainIdChange() (gas: 322841)\n[PASS] test_duplicateMarketIdCannotBeReusedAfterSettlement() (gas: 169863)\n[PASS] test_expiredSettlementRejected() (gas: 67504)\n[PASS] test_highSMalleatedSignatureRejected() (gas: 288305)\n[PASS] test_nonceIsGlobalAcrossMarketsWalletsAndSignerRotations() (gas: 410198)\n[PASS] test_ownerCannotSettleWithoutSigner() (gas: 86041)\n[PASS] test_personalSignSignatureRejected() (gas: 65513)\n[PASS] test_rotationRevokesAllOldSignatureTypesAndNewSignerWorks() (gas: 610660)\n[PASS] test_settlementDeadlineInclusiveAndPermissionless() (gas: 118455)\n[PASS] test_ticketExpiredRejectedEvenWithValidSignature() (gas: 56353)\n[PASS] test_ticketExpiryAtCurrentTimeAndMaximumAccepted() (gas: 385184)\n[PASS] test_ticketNonceCannotBeReusedEvenWithDifferentSignedFields() (gas: 347751)\n[PASS] test_ticketTooFarExpiryRejectedEvenWithValidSignature() (gas: 56817)\n[PASS] test_ticketWrongChain() (gas: 65369)\n[PASS] test_ticketWrongContract() (gas: 65144)\n[PASS] test_ticketWrongSigner() (gas: 65085)\nSuite result: ok. 27 passed; 0 failed; 0 skipped; finished in 224.49ms (582.30ms CPU time)\n\nRan 33 tests for test/BetPoolSafety.t.sol:BetPoolSafetyTest\n[PASS] test_IMDAmountsOutsideLimitsRejected() (gas: 251747)\n[PASS] test_SHIFTAmountsOutsideLimitsRejected() (gas: 252008)\n[PASS] test_bettingExclusiveCloseBoundary() (gas: 591966)\n[PASS] test_decisiveSettlementWaitsForCloseButVoidClosesBettingImmediately() (gas: 1898208)\n[PASS] test_duplicatePauseAndUnpauseRejectedAndConfigurationWorksWhilePaused() (gas: 222661)\n[PASS] test_factoryDeploymentWithoutAnyExternalContractsOrPrivileges() (gas: 2219063)\n[PASS] test_failedClaimPreservesEntitlementAndDoesNotBlockOthers() (gas: 1284617)\n[PASS] test_failedFeeTransferRollsBackSettlementAndCanExpireLater() (gas: 1328019)\n[PASS] test_heldTicketCannotBeSubmittedAheadOfTheOutcomeThatDecidesIt() (gas: 1393392)\n[PASS] test_inexactTransfersRevertAndRollBackEverything() (gas: 1311486)\n[PASS] test_insufficientAllowanceAndBalanceRollBack() (gas: 779584)\n[PASS] test_invalidOutcomeEvenIfSignedRejected() (gas: 131325)\n[PASS] test_invalidSideAndWeightRejectedEvenIfSignerApproves() (gas: 315574)\n[PASS] test_limitsCanReachButNeverExceedEitherHardCap() (gas: 276560)\n[PASS] test_minimumCanBeOneWeiButZeroShareTicketCannotCreateUndefinedPayout() (gas: 1087450)\n[PASS] test_newLimitsApplyToPendingTicketsAndExistingMarketsOnlyProspectively() (gas: 1120017)\n[PASS] test_noClaimsBeforeSettlementAndNoBetsOrDoubleSettleAfterwards() (gas: 719698)\n[PASS] test_onlyOwnerMayConfigureOrPause() (gas: 123401)\n[PASS] test_onlyTwoTokensAccepted() (gas: 112209)\n[PASS] test_ownerHasNoCustodyRedirectionOrOwnershipTransferFunctions() (gas: 741151)\n[PASS] test_pauseBlocksOnlyNewMarketsAndBets() (gas: 2280239)\n[PASS] test_reentrantBetCallbackIsBlocked() (gas: 681869)\n[PASS] test_reentrantClaimAndClaimManyCallbacksAreBlocked() (gas: 1413396)\n[PASS] test_reentrantSettlementFeeCallbackIsBlocked() (gas: 1153786)\n[PASS] test_runtimeSizeAndNoForbiddenOpcodes() (gas: 4513226)\n[PASS] test_safeERC20AcceptsNoReturnData() (gas: 1108338)\n[PASS] test_settleAfterExpireRejected() (gas: 207799)\n[PASS] test_settlementRefusedFromRefundDelayOnSoExpiryCannotBeRacedBySettle() (gas: 1479697)\n[PASS] test_signerCannotBeSetToZero() (gas: 43305)\n[PASS] test_tokenWithoutCodeCannotAcceptBet() (gas: 169825)\n[PASS] test_transferFromFailureDoesNotConsumeNonceOrRecordBet() (gas: 1103975)\n[PASS] test_twentyWeiMinimumWorksAtSmallestWeight() (gas: 1102041)\n[PASS] test_unknownMarketsRejectAllMutationsAndViewsReturnZero() (gas: 196863)\nSuite result: ok. 33 passed; 0 failed; 0 skipped; finished in 225.70ms (43.21ms CPU time)\n\nRan 21 tests for test/BetPoolFlows.t.sol:BetPoolFlowsTest\n[PASS] test_claimManyDuplicateRollsBackEntireBatch() (gas: 813741)\n[PASS] test_claimManyMixesTokensAndLosingClaims() (gas: 2303369)\n[PASS] test_claimManyUnknownOrUnsettledRollsBack() (gas: 904556)\n[PASS] test_constructorDefaultsIncludePublishedNightshiftSigner() (gas: 300260)\n[PASS] test_emptyMarketCanSettleAllOutcomes() (gas: 799560)\n[PASS] test_exactPayoutsAndFeeForSimpleWeights() (gas: 1528530)\n[PASS] test_expireEmptyMarket() (gas: 210270)\n[PASS] test_expiryAtBoundaryRefundsAndFinalizes() (gas: 1477724)\n[PASS] test_fullWeightedFlowIMD_AWins() (gas: 3671777)\n[PASS] test_fullWeightedFlowIMD_BWins() (gas: 3672976)\n[PASS] test_fullWeightedFlowSHIFT_AWins() (gas: 3672556)\n[PASS] test_fullWeightedFlowSHIFT_BWins() (gas: 3673028)\n[PASS] test_losersAndNonBettorsReceiveNothingAndCannotClaimTwice() (gas: 1258445)\n[PASS] test_marketPotsAreIndependentAndDonationsDoNotChangePayouts() (gas: 1723494)\n[PASS] test_oddLosingAmountRoundsFeeDownAndOnlyBonusCreatesDust() (gas: 1469251)\n[PASS] test_oneSidedBRefundedEvenWhenAIsDeclaredWinner() (gas: 677969)\n[PASS] test_oneSidedLosingSideRefunded() (gas: 1102665)\n[PASS] test_oneSidedVoidRefunded() (gas: 1097633)\n[PASS] test_oneSidedWinningSideRefunded() (gas: 1102678)\n[PASS] test_voidRefundsBothSidesIMD() (gas: 1421759)\n[PASS] test_voidRefundsBothSidesSHIFT() (gas: 1421818)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 225.76ms (35.49ms CPU time)\n\nRan 2 tests for test/BetPoolFuzz.t.sol:BetPoolFuzzTest\n[PASS] testFuzz_limitsWithinHardCapsNeverOverflowShares(bool,uint128,uint16,bool) (runs: 512, μ: 1117095, ~: 1117055)\nLogs:\n  Bound result 1000000000000000000\n  Bound result 6449\n\n[PASS] testFuzz_payoutsFollowWeightedSharesAndConserveBothTokens(uint128[8],uint16[8],bool,uint8,bool,bool) (runs: 512, μ: 5491702, ~: 6621096)\nLogs:\n  Bound result 0\n  Bound result 100000000000000002\n  Bound result 1396\n  Bound result 190201705729447738196\n  Bound result 8647\n  Bound result 499900000000002824400\n  Bound result 9507\n  Bound result 246713227179482250541\n  Bound result 9579\n  Bound result 499900000000000023822\n  Bound result 9696\n  Bound result 170931220433323550814\n  Bound result 2535\n  Bound result 189068544364349365541\n  Bound result 4072\n  Bound result 308528587173889502187\n  Bound result 5431\n  Bound result 499900000000000000013\n  Bound result 8526\n  Bound result 2794522138484845334\n  Bound result 6327\n  Bound result 308662938692120559874\n  Bound result 501\n  Bound result 382474486424091973676\n  Bound result 9514\n  Bound result 499953104653800217341\n  Bound result 500\n  Bound result 60613194625426682555\n  Bound result 500\n  Bound result 499900000000202913227\n  Bound result 501\n  Bound result 192911348331018626531\n  Bound result 9514\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 225.72ms (298.95ms CPU time)\n\nRan 1 test for test/BetPoolInvariant.t.sol:BetPoolInvariantTest\n[PASS]\nBetPoolInvariantTest invariants:\n[PASS] invariant_actualBalancesCoverAllMarketsAndConserveEveryWei\n[PASS] invariant_authorityAndNoncesRemainCorrect\n[PASS] invariant_marketStateAndEveryClaimMatchIndependentModel\n BetPoolInvariantTest invariants (runs: 128, calls: 12288, reverts: 0)\n\n╭----------------+--------------+-------+---------+----------╮\n| Contract       | Selector     | Calls | Reverts | Discards |\n+============================================================+\n| BetPoolHandler | adjustLimits | 1107  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | advance      | 1165  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | claim        | 1106  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | claimMany    | 1069  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | create       | 1088  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | donate       | 1181  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | expire       | 1061  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | place        | 1193  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | rotateSigner | 1133  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | settle       | 1106  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | togglePause  | 1079  | 0       | 0        |\n╰----------------+--------------+-------+---------+----------╯\n\nLogs:\n  Bound result 10000000000000000000\n  Bound result 10000\n  Bound result 10000000000000000000\n  Bound result 5000\n  Bound result 10000000000000000000\n  Bound result 4000\n  Bound result 10000000000000000000\n  Bound result 500\n  Bound result 1000000000000000000000000\n  Bound result 10000\n  Bound result 1000000000000000000000000\n  Bound result 5000\n  Bound result 1000000000000000000000000\n  Bound result 4000\n  Bound result 1000000000000000000000000\n  Bound result 500\n  Bound result 10000000000000000000\n  Bound result 10000\n  Bound result 10000000000000000000\n  Bound result 5000\n  Bound result 10000000000000000000\n  Bound result 4000\n  Bound result 10000000000000000000\n  Bound result 500\n  Bound result 1000000000000000000000000\n  Bound result 10000\n  Bound result 1000000000000000000000000\n  Bound result 5000\n  Bound result 1000000000000000000000000\n  Bound result 4000\n  Bound result 1000000000000000000000000\n  Bound result 500\n  Bound result 65279\n  Bound result 499999999999999999999943014\n  Bound result 499999999999999999999877749\n  Bound result 2849\n  Bound result 6569\n  Bound result 1\n  Bound result 9999999999999999999987\n  Bound result 9999999999999999999996\n  Bound result 4\n  Bound result 3963891462\n  Bound result 777\n  Bound result 8192\n  Bound result 2658\n  Bound result 5072\n  Bound result 86400\n  Bound result 499999999999999999999913665\n  Bound result 1\n  Bound result 9642\n  Bound result 9999999999999999990601\n  Bound result 1124\n  Bound result 152\n  Bound result 15311\n  Bound result 499999999999999999999828915\n  Bound result 502\n  Bound result 398\n  Bound result 500000000000000000000\n  Bound result 9596\n  Bound result 4917\n  Bound result 602302\n  Bound result 394892196923676441702320755\n  Bound result 417807781869536290929843294\n  Bound result 2\n  Bound result 1799\n  Bound result 355672\n  Bound result 405259479042112148068\n  Bound result 5327392583068602357338\n  Bound result 4922133104026490214272\n  Bound result 2671\n  Bound result 921\n  Bound result 12627\n  Bound result 9999999999999999997027\n  Bound result 10\n  Bound result 603546\n  Bound result 405692699322060444063776314\n  Bound result 471536503389698387335956274\n  Bound result 253827\n  Bound result 90\n  Bound result 2514000706\n  Bound result 499999999999999997486003391\n  Bound result 244\n  Bound result 51525089824\n  Bound result 505667\n  Bound result 499999999999999994972005031\n  Bound result 10000\n  Bound result 2\n  Bound result 0\n  Bound result 7674128\n  Bound result 15188\n  Bound result 10000000000000000000\n  Bound result 8827\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 30.69s (30.68s CPU time)\n\nRan 6 test suites in 30.69s (31.59s CPU time): 87 tests passed, 0 failed, 0 skipped (87 total tests)\n","passed":true},{"durationMs":43,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"BetPool.bet(bytes32,uint8,uint256,uint16,uint64,bytes32,bytes)\",\"BetPool.claim(bytes32)\",\"BetPool.claimMany(bytes32[])\",\"BetPool.createMarket(bytes32,address,uint64,bytes32,bytes)\",\"BetPool.expire(bytes32)\",\"BetPool.pause()\",\"BetPool.setLimits(address,uint256,uint256)\",\"BetPool.setSigner(address)\",\"BetPool.settle(bytes32,uint8,uint256,bytes)\",\"BetPool.unpause()\"],\"files\":{\".gitignore\":4,\"LICENSE\":21,\"README.md\":110,\"docs/DEPENDENCIES.md\":12,\"docs/REVIEW.md\":60,\"docs/eip712-types.json\":28,\"foundry.toml\":22,\"launch.json\":10,\"remappings.txt\":2,\"src/BetPool.sol\":367,\"test/BetPoolEvents.t.sol\":69,\"test/BetPoolFlows.t.sol\":338,\"test/BetPoolFuzz.t.sol\":115,\"test/BetPoolInvariant.t.sol\":125,\"test/BetPoolSafety.t.sol\":579,\"test/BetPoolSignatures.t.sol\":339,\"test/helpers/BetPoolHandler.sol\":298,\"test/helpers/BetPoolTestBase.sol\":144,\"test/helpers/MockToken.sol\":76},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"61c5ea96f51bd3b956db6d3bdf6f0da7646b12e021fb9355548f27fbedb2bd3b","verifiedTreeHash":"40f8698da7b7a3d7ed83913c680cd9d3e5b78768","verifierVersion":"0.1.0+a6ee6548"},{"checks":[{"durationMs":24335,"exitCode":0,"name":"build","output":"Compiling 48 files with Solc 0.8.26\nSolc 0.8.26 finished in 24.26s\nCompiler run successful!\n","passed":true},{"durationMs":29118,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 3 tests for test/BetPoolEvents.t.sol:BetPoolEventsTest\n[PASS] test_adminAndExpiryEvents() (gas: 327618)\n[PASS] test_marketBetSettlementAndClaimEvents() (gas: 1063752)\n[PASS] test_refundableSettlementEmitsZeroFee() (gas: 551103)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 22.62ms (2.87ms CPU time)\n\nRan 27 tests for test/BetPoolSignatures.t.sol:BetPoolSignaturesTest\n[PASS] testFuzz_createRejectsWrongSignerContractChain(uint8) (runs: 512, μ: 56592, ~: 56665)\nLogs:\n  Bound result 1\n\n[PASS] testFuzz_createSignatureBindsEveryField(uint8) (runs: 512, μ: 56425, ~: 56430)\nLogs:\n  Bound result 1\n\n[PASS] testFuzz_malformedSignatureLengthsRejected(uint8) (runs: 512, μ: 61931, ~: 61763)\nLogs:\n  Bound result 99\n\n[PASS] testFuzz_outcomeSignatureBindsEveryField(uint8) (runs: 512, μ: 142179, ~: 142296)\nLogs:\n  Bound result 1\n\n[PASS] testFuzz_settleRejectsWrongSignerContractChain(uint8) (runs: 512, μ: 55172, ~: 55236)\nLogs:\n  Bound result 0\n\n[PASS] testFuzz_ticketSignatureBindsEveryField(uint8) (runs: 512, μ: 105149, ~: 84243)\nLogs:\n  Bound result 0\n\n[PASS] test_badVAndZeroRecoveryRejected() (gas: 116404)\n[PASS] test_createSignatureCannotBeReplayedOnNewDeployment() (gas: 99751)\n[PASS] test_creationAtNowPastAndTooFarRejected() (gas: 124513)\n[PASS] test_creationAtSevenDaysAndPermissionlessSubmission() (gas: 117388)\n[PASS] test_domainAndTypeHashesMatchExactSpecification() (gas: 41207)\n[PASS] test_domainChangesAfterChainIdChange() (gas: 322913)\n[PASS] test_duplicateMarketIdCannotBeReusedAfterSettlement() (gas: 145384)\n[PASS] test_expiredSettlementRejected() (gas: 44787)\n[PASS] test_highSMalleatedSignatureRejected() (gas: 290489)\n[PASS] test_nonceIsGlobalAcrossMarketsWalletsAndSignerRotations() (gas: 410248)\n[PASS] test_ownerCannotSettleWithoutSigner() (gas: 65451)\n[PASS] test_personalSignSignatureRejected() (gas: 65549)\n[PASS] test_rotationRevokesAllOldSignatureTypesAndNewSignerWorks() (gas: 583254)\n[PASS] test_settlementDeadlineInclusiveAndPermissionless() (gas: 95759)\n[PASS] test_ticketExpiredRejectedEvenWithValidSignature() (gas: 56353)\n[PASS] test_ticketExpiryAtCurrentTimeAndMaximumAccepted() (gas: 385256)\n[PASS] test_ticketNonceCannotBeReusedEvenWithDifferentSignedFields() (gas: 347765)\n[PASS] test_ticketTooFarExpiryRejectedEvenWithValidSignature() (gas: 56817)\n[PASS] test_ticketWrongChain() (gas: 65405)\n[PASS] test_ticketWrongContract() (gas: 65180)\n[PASS] test_ticketWrongSigner() (gas: 65121)\nSuite result: ok. 27 passed; 0 failed; 0 skipped; finished in 200.36ms (421.51ms CPU time)\n\nRan 21 tests for test/BetPoolFlows.t.sol:BetPoolFlowsTest\n[PASS] test_claimManyDuplicateRollsBackEntireBatch() (gas: 788988)\n[PASS] test_claimManyMixesTokensAndLosingClaims() (gas: 2224954)\n[PASS] test_claimManyUnknownOrUnsettledRollsBack() (gas: 879710)\n[PASS] test_constructorDefaultsAndUnconfiguredSigner() (gas: 126474)\n[PASS] test_emptyMarketCanSettleAllOutcomes() (gas: 723067)\n[PASS] test_exactPayoutsAndFeeForSimpleWeights() (gas: 1500087)\n[PASS] test_expireEmptyMarket() (gas: 210090)\n[PASS] test_expiryAtBoundaryRefundsAndFinalizes() (gas: 1477426)\n[PASS] test_fullWeightedFlowIMD_AWins() (gas: 3645653)\n[PASS] test_fullWeightedFlowIMD_BWins() (gas: 3646874)\n[PASS] test_fullWeightedFlowSHIFT_AWins() (gas: 3646454)\n[PASS] test_fullWeightedFlowSHIFT_BWins() (gas: 3646926)\n[PASS] test_losersAndNonBettorsReceiveNothingAndCannotClaimTwice() (gas: 1229993)\n[PASS] test_marketPotsAreIndependentAndDonationsDoNotChangePayouts() (gas: 1672859)\n[PASS] test_oddLosingAmountRoundsFeeDownAndOnlyBonusCreatesDust() (gas: 1440830)\n[PASS] test_oneSidedBRefundedEvenWhenAIsDeclaredWinner() (gas: 649596)\n[PASS] test_oneSidedLosingSideRefunded() (gas: 1076725)\n[PASS] test_oneSidedVoidRefunded() (gas: 1072781)\n[PASS] test_oneSidedWinningSideRefunded() (gas: 1076738)\n[PASS] test_voidRefundsBothSidesIMD() (gas: 1397004)\n[PASS] test_voidRefundsBothSidesSHIFT() (gas: 1397063)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 230.34ms (27.59ms CPU time)\n\nRan 31 tests for test/BetPoolSafety.t.sol:BetPoolSafetyTest\n[PASS] test_IMDAmountsOutsideLimitsRejected() (gas: 251662)\n[PASS] test_SHIFTAmountsOutsideLimitsRejected() (gas: 251857)\n[PASS] test_bettingExclusiveCloseBoundary() (gas: 591849)\n[PASS] test_duplicatePauseAndUnpauseRejectedAndConfigurationWorksWhilePaused() (gas: 222573)\n[PASS] test_factoryDeploymentWithoutAnyExternalContractsOrPrivileges() (gas: 2165504)\n[PASS] test_failedClaimPreservesEntitlementAndDoesNotBlockOthers() (gas: 1257451)\n[PASS] test_failedFeeTransferRollsBackSettlementAndCanExpireLater() (gas: 1302548)\n[PASS] test_inexactTransfersRevertAndRollBackEverything() (gas: 1311330)\n[PASS] test_insufficientAllowanceAndBalanceRollBack() (gas: 779492)\n[PASS] test_invalidOutcomeEvenIfSignedRejected() (gas: 131337)\n[PASS] test_invalidSideAndWeightRejectedEvenIfSignerApproves() (gas: 315445)\n[PASS] test_limitsCanReachButNeverExceedEitherHardCap() (gas: 276307)\n[PASS] test_minimumCanBeOneWeiButZeroShareTicketCannotCreateUndefinedPayout() (gas: 1059123)\n[PASS] test_newLimitsApplyToPendingTicketsAndExistingMarketsOnlyProspectively() (gas: 1094218)\n[PASS] test_noClaimsBeforeSettlementAndNoBetsOrDoubleSettleAfterwards() (gas: 691919)\n[PASS] test_onlyOwnerMayConfigureOrPause() (gas: 123335)\n[PASS] test_onlyTwoTokensAccepted() (gas: 112165)\n[PASS] test_ownerHasNoCustodyRedirectionOrOwnershipTransferFunctions() (gas: 740882)\n[PASS] test_pauseBlocksOnlyNewMarketsAndBets() (gas: 2252258)\n[PASS] test_reentrantBetCallbackIsBlocked() (gas: 681798)\n[PASS] test_reentrantClaimAndClaimManyCallbacksAreBlocked() (gas: 1386208)\n[PASS] test_reentrantSettlementFeeCallbackIsBlocked() (gas: 1123521)\n[PASS] test_runtimeSizeAndNoForbiddenOpcodes() (gas: 4447414)\n[PASS] test_safeERC20AcceptsNoReturnData() (gas: 1080076)\n[PASS] test_settleAfterExpireRejected() (gas: 207722)\n[PASS] test_settlementAfterRefundDeadlineStillWorksUntilSomeoneExpires() (gas: 1057974)\n[PASS] test_signerCannotBeSetToZero() (gas: 43239)\n[PASS] test_tokenWithoutCodeCannotAcceptBet() (gas: 169842)\n[PASS] test_transferFromFailureDoesNotConsumeNonceOrRecordBet() (gas: 1103820)\n[PASS] test_twentyWeiMinimumWorksAtSmallestWeight() (gas: 1076278)\n[PASS] test_unknownMarketsRejectAllMutationsAndViewsReturnZero() (gas: 196772)\nSuite result: ok. 31 passed; 0 failed; 0 skipped; finished in 263.30ms (44.78ms CPU time)\n\nRan 2 tests for test/BetPoolFuzz.t.sol:BetPoolFuzzTest\n[PASS] testFuzz_limitsWithinHardCapsNeverOverflowShares(bool,uint128,uint16,bool) (runs: 512, μ: 1091431, ~: 1091400)\nLogs:\n  Bound result 3600\n  Bound result 755\n\n[PASS] testFuzz_payoutsFollowWeightedSharesAndConserveBothTokens(uint128[8],uint16[8],bool,uint8,bool,bool) (runs: 512, μ: 5466740, ~: 6595895)\nLogs:\n  Bound result 1\n  Bound result 24999000000000000000010000\n  Bound result 9505\n  Bound result 24999029000000000000000001\n  Bound result 5000\n  Bound result 24999000000000000000018424\n  Bound result 5669\n  Bound result 25000000000000000000000000\n  Bound result 9507\n  Bound result 24999009650000000000000001\n  Bound result 9600\n  Bound result 24999000000000000000001412\n  Bound result 6401\n  Bound result 25000000000000000000000000\n  Bound result 2724\n  Bound result 24999000000000000000009495\n  Bound result 9522\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 263.28ms (333.37ms CPU time)\n\nRan 1 test for test/BetPoolInvariant.t.sol:BetPoolInvariantTest\n[PASS]\nBetPoolInvariantTest invariants:\n[PASS] invariant_actualBalancesCoverAllMarketsAndConserveEveryWei\n[PASS] invariant_authorityAndNoncesRemainCorrect\n[PASS] invariant_marketStateAndEveryClaimMatchIndependentModel\n BetPoolInvariantTest invariants (runs: 128, calls: 12288, reverts: 0)\n\n╭----------------+--------------+-------+---------+----------╮\n| Contract       | Selector     | Calls | Reverts | Discards |\n+============================================================+\n| BetPoolHandler | adjustLimits | 1107  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | advance      | 1165  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | claim        | 1106  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | claimMany    | 1069  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | create       | 1088  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | donate       | 1181  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | expire       | 1061  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | place        | 1193  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | rotateSigner | 1133  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | settle       | 1106  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | togglePause  | 1079  | 0       | 0        |\n╰----------------+--------------+-------+---------+----------╯\n\nLogs:\n  Bound result 10000000000000000000\n  Bound result 10000\n  Bound result 10000000000000000000\n  Bound result 5000\n  Bound result 10000000000000000000\n  Bound result 4000\n  Bound result 10000000000000000000\n  Bound result 500\n  Bound result 1000000000000000000000000\n  Bound result 10000\n  Bound result 1000000000000000000000000\n  Bound result 5000\n  Bound result 1000000000000000000000000\n  Bound result 4000\n  Bound result 1000000000000000000000000\n  Bound result 500\n  Bound result 10000000000000000000\n  Bound result 10000\n  Bound result 10000000000000000000\n  Bound result 5000\n  Bound result 10000000000000000000\n  Bound result 4000\n  Bound result 10000000000000000000\n  Bound result 500\n  Bound result 1000000000000000000000000\n  Bound result 10000\n  Bound result 1000000000000000000000000\n  Bound result 5000\n  Bound result 1000000000000000000000000\n  Bound result 4000\n  Bound result 1000000000000000000000000\n  Bound result 500\n  Bound result 895\n  Bound result 8251\n  Bound result 2987\n  Bound result 2363\n  Bound result 6716\n  Bound result 0\n  Bound result 5000000000000000000\n  Bound result 9995000000000000007382\n  Bound result 2900000000000000000\n  Bound result 4143\n  Bound result 11000000000000000000\n  Bound result 7531\n  Bound result 1800\n  Bound result 5001\n  Bound result 27\n  Bound result 4905\n  Bound result 120\n  Bound result 499999999999999999999999945\n  Bound result 0\n  Bound result 6722540745621196690441\n  Bound result 9832377763136409928935\n  Bound result 1085\n  Bound result 658\n  Bound result 6222\n  Bound result 13\n  Bound result 528174651\n  Bound result 603546\n  Bound result 394892196923676441702320755\n  Bound result 417807781869536290929843294\n  Bound result 2\n  Bound result 1356\n  Bound result 406076023262212748856756433\n  Bound result 8392\n  Bound result 601214\n  Bound result 405259479042112148068\n  Bound result 5327392583068602357338\n  Bound result 921\n  Bound result 12567\n  Bound result 9999999999999999997217\n  Bound result 8192\n  Bound result 601222\n  Bound result 405692699322060444063776314\n  Bound result 471536503389698387335956274\n  Bound result 253827\n  Bound result 1496\n  Bound result 2514000706\n  Bound result 3000000000000000000\n  Bound result 0\n  Bound result 10\n  Bound result 51525089824\n  Bound result 505667\n  Bound result 0\n  Bound result 20\n  Bound result 14706\n  Bound result 2900000000000000019\n  Bound result 8877\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 29.03s (29.02s CPU time)\n\nRan 6 test suites in 29.03s (30.01s CPU time): 85 tests passed, 0 failed, 0 skipped (85 total tests)\n","passed":true},{"durationMs":50,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"BetPool.bet(bytes32,uint8,uint256,uint16,uint64,bytes32,bytes)\",\"BetPool.claim(bytes32)\",\"BetPool.claimMany(bytes32[])\",\"BetPool.createMarket(bytes32,address,uint64,bytes32,bytes)\",\"BetPool.expire(bytes32)\",\"BetPool.pause()\",\"BetPool.setLimits(address,uint256,uint256)\",\"BetPool.setSigner(address)\",\"BetPool.settle(bytes32,uint8,uint256,bytes)\",\"BetPool.unpause()\"],\"files\":{\".gitignore\":4,\"LICENSE\":21,\"README.md\":104,\"docs/DEPENDENCIES.md\":12,\"docs/REVIEW.md\":56,\"docs/eip712-types.json\":28,\"foundry.toml\":22,\"remappings.txt\":2,\"src/BetPool.sol\":353,\"test/BetPoolEvents.t.sol\":67,\"test/BetPoolFlows.t.sol\":326,\"test/BetPoolFuzz.t.sol\":115,\"test/BetPoolInvariant.t.sol\":125,\"test/BetPoolSafety.t.sol\":481,\"test/BetPoolSignatures.t.sol\":333,\"test/helpers/BetPoolHandler.sol\":295,\"test/helpers/BetPoolTestBase.sol\":140,\"test/helpers/MockToken.sol\":76},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":1728,"exitCode":0,"name":"slither","output":"[high/medium] reentrancy-balance at src/BetPool.sol:189: Reentrancy in BetPool.bet(bytes32,uint8,uint256,uint16,uint64,bytes32,bytes) (src/BetPool.sol#189-226):\n[medium/medium] unused-return at src/BetPool.sol:334: BetPool._verify(bytes32,bytes) (src/BetPool.sol#334-339) ignores return value by (recovered,error,None) = ECDSA.tryRecover(_hashTypedDataV4(structHash),sig) (src/BetPool.sol#337)\n[low/medium] timestamp at src/BetPool.sol:164: BetPool.createMarket(bytes32,address,uint64,bytes32,bytes) (src/BetPool.sol#164-180) uses timestamp for comparisons\n[low/medium] timestamp at src/BetPool.sol:189: BetPool.bet(bytes32,uint8,uint256,uint16,uint64,bytes32,bytes) (src/BetPool.sol#189-226) uses timestamp for comparisons\n[low/medium] timestamp at src/BetPool.sol:232: BetPool.settle(bytes32,uint8,uint256,bytes) (src/BetPool.sol#232-251) uses timestamp for comparisons\n[low/medium] timestamp at src/BetPool.sol:255: BetPool.expire(bytes32) (src/BetPool.sol#255-263) uses timestamp for comparisons","passed":true},{"durationMs":386,"exitCode":0,"name":"aderyn","output":"[high] reentrancy-state-change at src/BetPool.sol:219: Reentrancy: State change after external call\n[low] centralization-risk at src/BetPool.sol:136: Centralization Risk (4 places)\n[low] costly-loop at src/BetPool.sol:275: Costly operations inside loop\n[low] large-numeric-literal at src/BetPool.sol:27: Large Numeric Literal (5 places)\n[low] literal-instead-of-constant at src/BetPool.sol:246: Literal Instead of Constant (2 places)\n[low] require-revert-in-loop at src/BetPool.sol:275: Loop Contains `require`/`revert`\n[low] unchecked-return at src/BetPool.sol:170: Unchecked Return (2 places)\n[low] uninitialized-local-variable at src/BetPool.sol:275: Uninitialized Local Variable","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"b64ca97f5339a2b3a41c711bb729d5e0ab0a74fb62805bd3871cfa9525be6940","verifiedTreeHash":"42c51d522f001edcfd806227361fb5a0ae4c1c30","verifierVersion":"0.1.0+a6ee6548"},{"checks":[{"durationMs":36894,"exitCode":0,"name":"build","output":"Compiling 51 files with Solc 0.8.26\nSolc 0.8.26 finished in 36.81s\nCompiler run successful!\n","passed":true},{"durationMs":30621,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 3 tests for test/BetPoolEvents.t.sol:BetPoolEventsTest\n[PASS] test_adminAndExpiryEvents() (gas: 327618)\n[PASS] test_marketBetSettlementAndClaimEvents() (gas: 1063752)\n[PASS] test_refundableSettlementEmitsZeroFee() (gas: 551103)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 3.33ms (1.84ms CPU time)\n\nRan 18 tests for test/BetPoolOracle.t.sol:BetPoolOracleTest\n[PASS] testFuzz_randomKeyNeverAuthorizes(uint256,uint8) (runs: 256, μ: 372523, ~: 372619)\nLogs:\n  Bound result 115792089237316195423570985008687907852837564279074904382605163141518161494335\n  Bound result 0\n\n[PASS] testFuzz_winnerGetsPrincipalPlusAtMostNetLosingSide(uint256,uint256,uint256,uint16,uint16,bool,bool) (runs: 256, μ: 1476648, ~: 1476687)\nLogs:\n  Bound result 13229846805346861482279490\n  Bound result 24999000000000000000058216\n  Bound result 24999000000000000000003955\n  Bound result 5590\n  Bound result 7777\n\n[PASS] test_betRefusedAtCloseAfterEarlySettlementAndAfterExpiry() (gas: 1837183)\n[PASS] test_claimEdgeCases() (gas: 1326036)\n[PASS] test_createRefusesWrongFieldsAndUnsupportedToken() (gas: 1492025)\n[PASS] test_feeRoundsDownOnOddTinyLosingSide() (gas: 1056296)\n[PASS] test_feeZeroWhenLosingSideBelowTwentyWei() (gas: 1880149)\n[PASS] test_oracleBothMarketsVoidRefundExactly() (gas: 5067320)\n[PASS] test_oracleIMD_AWins() (gas: 2769519)\n[PASS] test_oracleIMD_BWins() (gas: 2749634)\n[PASS] test_oracleSHIFT_AWins() (gas: 2488021)\n[PASS] test_oracleSHIFT_BWins() (gas: 2488182)\n[PASS] test_ownerCannotTakeFundsThroughAnyRoute() (gas: 1518653)\n[PASS] test_pauseBlocksCreateAndBetOnly() (gas: 1494722)\n[PASS] test_settleWithSignatureForOtherMarketOrOutcomeRefused() (gas: 1267075)\n[PASS] test_signedButOutOfRangeTicketsRefused() (gas: 3368156)\n[PASS] test_ticketExpiryBoundaries() (gas: 700806)\n[PASS] test_ticketForOneSideOrAmountCannotBeUsedForAnother() (gas: 943831)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 108.04ms (223.45ms CPU time)\n\nRan 27 tests for test/BetPoolSignatures.t.sol:BetPoolSignaturesTest\n[PASS] testFuzz_createRejectsWrongSignerContractChain(uint8) (runs: 512, μ: 56592, ~: 56676)\nLogs:\n  Bound result 1\n\n[PASS] testFuzz_createSignatureBindsEveryField(uint8) (runs: 512, μ: 56428, ~: 56430)\nLogs:\n  Bound result 1\n\n[PASS] testFuzz_malformedSignatureLengthsRejected(uint8) (runs: 512, μ: 61929, ~: 61763)\nLogs:\n  Bound result 49\n\n[PASS] testFuzz_outcomeSignatureBindsEveryField(uint8) (runs: 512, μ: 142177, ~: 142296)\nLogs:\n  Bound result 1\n\n[PASS] testFuzz_settleRejectsWrongSignerContractChain(uint8) (runs: 512, μ: 55172, ~: 55248)\nLogs:\n  Bound result 1\n\n[PASS] testFuzz_ticketSignatureBindsEveryField(uint8) (runs: 512, μ: 104420, ~: 84243)\nLogs:\n  Bound result 0\n\n[PASS] test_badVAndZeroRecoveryRejected() (gas: 116404)\n[PASS] test_createSignatureCannotBeReplayedOnNewDeployment() (gas: 99751)\n[PASS] test_creationAtNowPastAndTooFarRejected() (gas: 124513)\n[PASS] test_creationAtSevenDaysAndPermissionlessSubmission() (gas: 117388)\n[PASS] test_domainAndTypeHashesMatchExactSpecification() (gas: 41207)\n[PASS] test_domainChangesAfterChainIdChange() (gas: 322913)\n[PASS] test_duplicateMarketIdCannotBeReusedAfterSettlement() (gas: 145384)\n[PASS] test_expiredSettlementRejected() (gas: 44787)\n[PASS] test_highSMalleatedSignatureRejected() (gas: 290489)\n[PASS] test_nonceIsGlobalAcrossMarketsWalletsAndSignerRotations() (gas: 410248)\n[PASS] test_ownerCannotSettleWithoutSigner() (gas: 65451)\n[PASS] test_personalSignSignatureRejected() (gas: 65549)\n[PASS] test_rotationRevokesAllOldSignatureTypesAndNewSignerWorks() (gas: 583254)\n[PASS] test_settlementDeadlineInclusiveAndPermissionless() (gas: 95759)\n[PASS] test_ticketExpiredRejectedEvenWithValidSignature() (gas: 56353)\n[PASS] test_ticketExpiryAtCurrentTimeAndMaximumAccepted() (gas: 385256)\n[PASS] test_ticketNonceCannotBeReusedEvenWithDifferentSignedFields() (gas: 347765)\n[PASS] test_ticketTooFarExpiryRejectedEvenWithValidSignature() (gas: 56817)\n[PASS] test_ticketWrongChain() (gas: 65405)\n[PASS] test_ticketWrongContract() (gas: 65180)\n[PASS] test_ticketWrongSigner() (gas: 65121)\nSuite result: ok. 27 passed; 0 failed; 0 skipped; finished in 108.06ms (412.30ms CPU time)\n\nRan 21 tests for test/BetPoolFlows.t.sol:BetPoolFlowsTest\n[PASS] test_claimManyDuplicateRollsBackEntireBatch() (gas: 788988)\n[PASS] test_claimManyMixesTokensAndLosingClaims() (gas: 2224954)\n[PASS] test_claimManyUnknownOrUnsettledRollsBack() (gas: 879710)\n[PASS] test_constructorDefaultsAndUnconfiguredSigner() (gas: 126474)\n[PASS] test_emptyMarketCanSettleAllOutcomes() (gas: 723067)\n[PASS] test_exactPayoutsAndFeeForSimpleWeights() (gas: 1500087)\n[PASS] test_expireEmptyMarket() (gas: 210090)\n[PASS] test_expiryAtBoundaryRefundsAndFinalizes() (gas: 1477426)\n[PASS] test_fullWeightedFlowIMD_AWins() (gas: 3645653)\n[PASS] test_fullWeightedFlowIMD_BWins() (gas: 3646874)\n[PASS] test_fullWeightedFlowSHIFT_AWins() (gas: 3646454)\n[PASS] test_fullWeightedFlowSHIFT_BWins() (gas: 3646926)\n[PASS] test_losersAndNonBettorsReceiveNothingAndCannotClaimTwice() (gas: 1229993)\n[PASS] test_marketPotsAreIndependentAndDonationsDoNotChangePayouts() (gas: 1672859)\n[PASS] test_oddLosingAmountRoundsFeeDownAndOnlyBonusCreatesDust() (gas: 1440830)\n[PASS] test_oneSidedBRefundedEvenWhenAIsDeclaredWinner() (gas: 649596)\n[PASS] test_oneSidedLosingSideRefunded() (gas: 1076725)\n[PASS] test_oneSidedVoidRefunded() (gas: 1072781)\n[PASS] test_oneSidedWinningSideRefunded() (gas: 1076738)\n[PASS] test_voidRefundsBothSidesIMD() (gas: 1397004)\n[PASS] test_voidRefundsBothSidesSHIFT() (gas: 1397063)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 243.60ms (25.66ms CPU time)\n\nRan 31 tests for test/BetPoolSafety.t.sol:BetPoolSafetyTest\n[PASS] test_IMDAmountsOutsideLimitsRejected() (gas: 251662)\n[PASS] test_SHIFTAmountsOutsideLimitsRejected() (gas: 251857)\n[PASS] test_bettingExclusiveCloseBoundary() (gas: 591849)\n[PASS] test_duplicatePauseAndUnpauseRejectedAndConfigurationWorksWhilePaused() (gas: 222573)\n[PASS] test_factoryDeploymentWithoutAnyExternalContractsOrPrivileges() (gas: 2165504)\n[PASS] test_failedClaimPreservesEntitlementAndDoesNotBlockOthers() (gas: 1257451)\n[PASS] test_failedFeeTransferRollsBackSettlementAndCanExpireLater() (gas: 1302548)\n[PASS] test_inexactTransfersRevertAndRollBackEverything() (gas: 1311330)\n[PASS] test_insufficientAllowanceAndBalanceRollBack() (gas: 779492)\n[PASS] test_invalidOutcomeEvenIfSignedRejected() (gas: 131337)\n[PASS] test_invalidSideAndWeightRejectedEvenIfSignerApproves() (gas: 315445)\n[PASS] test_limitsCanReachButNeverExceedEitherHardCap() (gas: 276307)\n[PASS] test_minimumCanBeOneWeiButZeroShareTicketCannotCreateUndefinedPayout() (gas: 1059123)\n[PASS] test_newLimitsApplyToPendingTicketsAndExistingMarketsOnlyProspectively() (gas: 1094218)\n[PASS] test_noClaimsBeforeSettlementAndNoBetsOrDoubleSettleAfterwards() (gas: 691919)\n[PASS] test_onlyOwnerMayConfigureOrPause() (gas: 123335)\n[PASS] test_onlyTwoTokensAccepted() (gas: 112165)\n[PASS] test_ownerHasNoCustodyRedirectionOrOwnershipTransferFunctions() (gas: 740882)\n[PASS] test_pauseBlocksOnlyNewMarketsAndBets() (gas: 2252258)\n[PASS] test_reentrantBetCallbackIsBlocked() (gas: 681798)\n[PASS] test_reentrantClaimAndClaimManyCallbacksAreBlocked() (gas: 1386208)\n[PASS] test_reentrantSettlementFeeCallbackIsBlocked() (gas: 1123521)\n[PASS] test_runtimeSizeAndNoForbiddenOpcodes() (gas: 4447414)\n[PASS] test_safeERC20AcceptsNoReturnData() (gas: 1080076)\n[PASS] test_settleAfterExpireRejected() (gas: 207722)\n[PASS] test_settlementAfterRefundDeadlineStillWorksUntilSomeoneExpires() (gas: 1057974)\n[PASS] test_signerCannotBeSetToZero() (gas: 43239)\n[PASS] test_tokenWithoutCodeCannotAcceptBet() (gas: 169842)\n[PASS] test_transferFromFailureDoesNotConsumeNonceOrRecordBet() (gas: 1103820)\n[PASS] test_twentyWeiMinimumWorksAtSmallestWeight() (gas: 1076278)\n[PASS] test_unknownMarketsRejectAllMutationsAndViewsReturnZero() (gas: 196772)\nSuite result: ok. 31 passed; 0 failed; 0 skipped; finished in 269.57ms (51.01ms CPU time)\n\nRan 2 tests for test/BetPoolFuzz.t.sol:BetPoolFuzzTest\n[PASS] testFuzz_limitsWithinHardCapsNeverOverflowShares(bool,uint128,uint16,bool) (runs: 512, μ: 1091443, ~: 1091400)\nLogs:\n  Bound result 2345810831772884312383\n  Bound result 9560\n\n[PASS] testFuzz_payoutsFollowWeightedSharesAndConserveBothTokens(uint128[8],uint16[8],bool,uint8,bool,bool) (runs: 512, μ: 5466135, ~: 6596025)\nLogs:\n  Bound result 2\n  Bound result 499900008771918567376\n  Bound result 502\n  Bound result 65513288992946884271\n  Bound result 9527\n  Bound result 499900000000000000340\n  Bound result 7423\n  Bound result 433715235272932548548\n  Bound result 1489\n  Bound result 412039513864223635918\n  Bound result 8529\n  Bound result 443416636302403326566\n  Bound result 6366\n  Bound result 169657578721898763319\n  Bound result 9510\n  Bound result 411037072963334929908\n  Bound result 9618\n  Bound result 499900000000007204305\n  Bound result 9716\n  Bound result 250427678789683454831\n  Bound result 2795\n  Bound result 499900000000525064855\n  Bound result 9751\n  Bound result 377094652023263565948\n  Bound result 3250\n  Bound result 499901052565222364583\n  Bound result 9577\n  Bound result 206431721851232220096\n  Bound result 988\n  Bound result 240182330387147649805\n  Bound result 3519\n  Bound result 226399378994171319233\n  Bound result 7743\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 269.37ms (346.19ms CPU time)\n\nRan 1 test for test/BetPoolAttackInvariant.t.sol:BetPoolAttackInvariantTest\n[PASS]\nBetPoolAttackInvariantTest invariants:\n[PASS] invariant_finishedMarketsStayFinalAndStrangersGetNothing\n[PASS] invariant_poolHoldsWhatItOwesAndNeverOverpays\n BetPoolAttackInvariantTest invariants (runs: 64, calls: 4096, reverts: 0)\n\n╭----------------------+------------------+-------+---------+----------╮\n| Contract             | Selector         | Calls | Reverts | Discards |\n+======================================================================+\n| BetPoolAttackHandler | advance          | 304   | 0       | 0        |\n|----------------------+------------------+-------+---------+----------|\n| BetPoolAttackHandler | claim            | 300   | 0       | 0        |\n|----------------------+------------------+-------+---------+----------|\n| BetPoolAttackHandler | create           | 288   | 0       | 0        |\n|----------------------+------------------+-------+---------+----------|\n| BetPoolAttackHandler | expire           | 278   | 0       | 0        |\n|----------------------+------------------+-------+---------+----------|\n| BetPoolAttackHandler | forgedSettle     | 298   | 0       | 0        |\n|----------------------+------------------+-------+---------+----------|\n| BetPoolAttackHandler | forgedTicket     | 292   | 0       | 0        |\n|----------------------+------------------+-------+---------+----------|\n| BetPoolAttackHandler | place            | 574   | 0       | 0        |\n|----------------------+------------------+-------+---------+----------|\n| BetPoolAttackHandler | replayLastTicket | 291   | 0       | 0        |\n|----------------------+------------------+-------+---------+----------|\n| BetPoolAttackHandler | resettle         | 304   | 0       | 0        |\n|----------------------+------------------+-------+---------+----------|\n| BetPoolAttackHandler | settle           | 323   | 0       | 0        |\n|----------------------+------------------+-------+---------+----------|\n| BetPoolAttackHandler | stealTicket      | 305   | 0       | 0        |\n|----------------------+------------------+-------+---------+----------|\n| BetPoolAttackHandler | strangerAdmin    | 263   | 0       | 0        |\n|----------------------+------------------+-------+---------+----------|\n| BetPoolAttackHandler | strangerClaims   | 276   | 0       | 0        |\n╰----------------------+------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 13062\n  Bound result 29000000000000000001\n  Bound result 11577882133231603377425915\n  Bound result 9523\n  Bound result 499900000000000000012\n  Bound result 9520\n  Bound result 7042586554550355135155\n  Bound result 583603\n  Bound result 2673\n  Bound result 499900000000000010172\n  Bound result 9513\n  Bound result 499900000000000007604\n  Bound result 9505\n  Bound result 24999001110844516972235139\n  Bound result 9500\n  Bound result 0\n  Bound result 1794\n  Bound result 7000000000000000000\n  Bound result 1299\n  Bound result 269614\n  Bound result 29000000000000000001\n  Bound result 2345\n  Bound result 24999000000000000000000754\n  Bound result 1647\n  Bound result 0\n  Bound result 9845\n  Bound result 26503\n  Bound result 499900000000000000301\n  Bound result 3828\n  Bound result 6504\n  Bound result 65249507753603018240\n  Bound result 9696\n  Bound result 2445\n  Bound result 499900000000000006638\n  Bound result 9521\n  Bound result 6000\n  Bound result 499900000000000019486\n  Bound result 7777\n  Bound result 601218\n  Bound result 499900000000000000020\n  Bound result 9521\n  Bound result 145250\n  Bound result 5870\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.41s (3.41s CPU time)\n\nRan 1 test for test/BetPoolInvariant.t.sol:BetPoolInvariantTest\n[PASS]\nBetPoolInvariantTest invariants:\n[PASS] invariant_actualBalancesCoverAllMarketsAndConserveEveryWei\n[PASS] invariant_authorityAndNoncesRemainCorrect\n[PASS] invariant_marketStateAndEveryClaimMatchIndependentModel\n BetPoolInvariantTest invariants (runs: 128, calls: 12288, reverts: 0)\n\n╭----------------+--------------+-------+---------+----------╮\n| Contract       | Selector     | Calls | Reverts | Discards |\n+============================================================+\n| BetPoolHandler | adjustLimits | 1107  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | advance      | 1165  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | claim        | 1106  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | claimMany    | 1069  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | create       | 1088  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | donate       | 1181  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | expire       | 1061  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | place        | 1193  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | rotateSigner | 1133  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | settle       | 1106  | 0       | 0        |\n|----------------+--------------+-------+---------+----------|\n| BetPoolHandler | togglePause  | 1079  | 0       | 0        |\n╰----------------+--------------+-------+---------+----------╯\n\nLogs:\n  Bound result 10000000000000000000\n  Bound result 10000\n  Bound result 10000000000000000000\n  Bound result 5000\n  Bound result 10000000000000000000\n  Bound result 4000\n  Bound result 10000000000000000000\n  Bound result 500\n  Bound result 1000000000000000000000000\n  Bound result 10000\n  Bound result 1000000000000000000000000\n  Bound result 5000\n  Bound result 1000000000000000000000000\n  Bound result 4000\n  Bound result 1000000000000000000000000\n  Bound result 500\n  Bound result 10000000000000000000\n  Bound result 10000\n  Bound result 10000000000000000000\n  Bound result 5000\n  Bound result 10000000000000000000\n  Bound result 4000\n  Bound result 10000000000000000000\n  Bound result 500\n  Bound result 1000000000000000000000000\n  Bound result 10000\n  Bound result 1000000000000000000000000\n  Bound result 5000\n  Bound result 1000000000000000000000000\n  Bound result 4000\n  Bound result 1000000000000000000000000\n  Bound result 500\n  Bound result 895\n  Bound result 8251\n  Bound result 2398\n  Bound result 2363\n  Bound result 6716\n  Bound result 0\n  Bound result 1110844516972235138\n  Bound result 9998889155483027772243\n  Bound result 1000000000000000000\n  Bound result 4143\n  Bound result 0\n  Bound result 32778777000000000000000008\n  Bound result 467221254182839450617283938\n  Bound result 27\n  Bound result 4905\n  Bound result 1000000000000000000000\n  Bound result 499999000000000000000000029\n  Bound result 1356\n  Bound result 6722540745621196690441\n  Bound result 9832577763136409928681\n  Bound result 1085\n  Bound result 658\n  Bound result 6222\n  Bound result 329\n  Bound result 528174649\n  Bound result 601222\n  Bound result 394892196923676441702320755\n  Bound result 417807781869536290929843294\n  Bound result 12\n  Bound result 291672\n  Bound result 405259479042112148068\n  Bound result 5327392583068602357338\n  Bound result 921\n  Bound result 11823\n  Bound result 9999999999999999997694\n  Bound result 0\n  Bound result 35567000000000987654059\n  Bound result 69225\n  Bound result 405692699322060444063776314\n  Bound result 471536503389698387335956274\n  Bound result 253827\n  Bound result 1601\n  Bound result 2514000706\n  Bound result 578181333333333333332\n  Bound result 1765\n  Bound result 51525089824\n  Bound result 505667\n  Bound result 2900000000000000019\n  Bound result 9756\n  Bound result 0\n  Bound result 28672\n  Bound result 17000000000000000003\n  Bound result 9566\n  Bound result 193993421901600739369463306\n  Bound result 306006578098399260630536706\n  Bound result 224026312393597042522146810\n  Bound result 1171\n  Bound result 9072\n  Bound result 9000000000000000000000\n  Bound result 2838\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 30.52s (30.51s CPU time)\n\nRan 8 test suites in 30.52s (34.93s CPU time): 104 tests passed, 0 failed, 0 skipped (104 total tests)\n","passed":true},{"durationMs":51,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"BetPool.bet(bytes32,uint8,uint256,uint16,uint64,bytes32,bytes)\",\"BetPool.claim(bytes32)\",\"BetPool.claimMany(bytes32[])\",\"BetPool.createMarket(bytes32,address,uint64,bytes32,bytes)\",\"BetPool.expire(bytes32)\",\"BetPool.pause()\",\"BetPool.setLimits(address,uint256,uint256)\",\"BetPool.setSigner(address)\",\"BetPool.settle(bytes32,uint8,uint256,bytes)\",\"BetPool.unpause()\"],\"files\":{\".gitignore\":4,\"LICENSE\":21,\"README.md\":104,\"docs/DEPENDENCIES.md\":12,\"docs/REVIEW.md\":56,\"docs/eip712-types.json\":28,\"foundry.toml\":22,\"remappings.txt\":2,\"src/BetPool.sol\":353,\"test/BetPoolAttackInvariant.t.sol\":80,\"test/BetPoolEvents.t.sol\":67,\"test/BetPoolFlows.t.sol\":326,\"test/BetPoolFuzz.t.sol\":115,\"test/BetPoolInvariant.t.sol\":125,\"test/BetPoolOracle.t.sol\":508,\"test/BetPoolSafety.t.sol\":481,\"test/BetPoolSignatures.t.sol\":333,\"test/helpers/BetPoolAttackHandler.sol\":383,\"test/helpers/BetPoolHandler.sol\":295,\"test/helpers/BetPoolTestBase.sol\":140,\"test/helpers/MockToken.sol\":76},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"e237b435bfe486482a14ed9307497e0b09134550fd9501abf0462285660c7484","verifiedTreeHash":"3b582903893f034508b0eddf64ac648980d4837f","verifierVersion":"0.1.0+a6ee6548"}]}