{"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":"810ebb02-6305-475b-9aa3-72c0f4d22bd5","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"06a3c6f5c868ac0de01c7551849234bf54c2cf8c1dbad1b9bb67a896b08ee6ff","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":"72dd8197d262b3ed219720c37ae6c33c6f08350a53e221bc03104346accdf719","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":"e18fee2cc4e925ecc3d3f7578143e3064b1962b98449700f16e11f17c4d97af9","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":"2e1040e8d18911da74a77bc0965febf06f5cc1084863131184742f0f0b537c33","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":"7dbbe14e1e91475b2f3a0d987273584afe95a84d3504c9b07c26160b1e6f0ad7","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":"031c40f32b6cf1c8ffbadc9e7bbdd8d4b8ad2dc891a88b1cd1d30951834339e3","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":"cee47de1c0bf611222bab8632809684712b373b69cf40511075cb727b9390c56","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"71a07757f9e3812e709722585dc722f1ec8f2c1ed3f3c93adb4ef4b2b7c238a8","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":"A custom token: HAMSTER (HAMSTER).\nToken name: HAMSTER\nToken symbol: HAMSTER\nToken supply: 1,000,000,000 with 18 decimals, all minted once to the deployer in the constructor.\nWhat it does: bet on hamster, lose to earn $HAMSTER","parentJobId":null,"planHash":"ad004321066cbc4878eb337ddc9db3988691e28712456bd6fbc633243d81c7a6","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"810ebb02-6305-475b-9aa3-72c0f4d22bd5","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-2218-hamster"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51530","feedbackHash":"e6fb97f75a6897c1edd0aa174e0496b124dbbdf1d89434f6fa9d26214a00c489","nodeKey":"audit_economics","submissionHash":"06a3c6f5c868ac0de01c7551849234bf54c2cf8c1dbad1b9bb67a896b08ee6ff","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51025","feedbackHash":"c0f7e74a09af82e15240e3f4f180b9a9132a2cdda23574043e9dcc3f443306a6","nodeKey":"audit_flow","submissionHash":"72dd8197d262b3ed219720c37ae6c33c6f08350a53e221bc03104346accdf719","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52159","feedbackHash":"5de791c6a5d4e40a819f17bf92fa7d7510854c55e02f929e55dae1b27240fdb7","nodeKey":"audit_judge","submissionHash":"e18fee2cc4e925ecc3d3f7578143e3064b1962b98449700f16e11f17c4d97af9","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52188","feedbackHash":"2c712d7025d1dc0ed027a2583502298651fc902f33c3f019d77c32b0898b3184","nodeKey":"audit_judge","submissionHash":"3fbc97d2d9ccb1eb5a617049eeb9dbe444d0920b1c3ee61d7023d280747422b6","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"54237","feedbackHash":"8096283597f3066de7991753ddb3d6c3fc96cf3c62ac752a0688354296dd209b","nodeKey":"audit_math","submissionHash":"2e1040e8d18911da74a77bc0965febf06f5cc1084863131184742f0f0b537c33","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52270","feedbackHash":"65d4fa10957448d0bc0241105986cfd5c267d4a091f4c2c07decf6d95def2b27","nodeKey":"audit_permissions","submissionHash":"7dbbe14e1e91475b2f3a0d987273584afe95a84d3504c9b07c26160b1e6f0ad7","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51374","feedbackHash":"006f65252631b4fbce12b2c37f8a363bfee1254f0f3eacfd3b7d109394c28047","nodeKey":"build_contract_project","submissionHash":"031c40f32b6cf1c8ffbadc9e7bbdd8d4b8ad2dc891a88b1cd1d30951834339e3","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51870","feedbackHash":"b4b4a9df4d7a18dea4a32dbc4b237c9108df0d4aadd48504449816a039a278b3","nodeKey":"build_contract_project","submissionHash":"f5a54aa78be3b4ebad0c5dd0a9df886dfe414c84680c21047e1ce4194062de76","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51540","feedbackHash":"c8ed8bb4e6a17aac68dcafa41c3a6b8933018dafb333aeb9c6ba7a3d517f6c2c","nodeKey":"manifest","submissionHash":"cee47de1c0bf611222bab8632809684712b373b69cf40511075cb727b9390c56","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52287","feedbackHash":"c9e4fb8b0b06ad7757b8bd7b49762b43946e93f74b2641a276c0aea77ff85eee","nodeKey":"manifest","submissionHash":"57e3e16f7e6cf1c9cce2f17d0ae7bfe3d34fe6c50dae1d67c158e03374387695","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51883","feedbackHash":"c43be949c72a58cd4d307939197bc5de51e14e2caf3bdb5025205b7801d1be77","nodeKey":"write_foundry_tests","submissionHash":"d5f8bb60c7262805b98920c056fb1e69148668ccdf52bd142ae3901f740c4dc1","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52200","feedbackHash":"e365faaf7031be1552a15af216295fbe9e18b94f8221fbdf5aac174c31975f20","nodeKey":"write_foundry_tests","submissionHash":"71a07757f9e3812e709722585dc722f1ec8f2c1ed3f3c93adb4ef4b2b7c238a8","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"b82a82a4a5557cbc5281d01451f3c86477d34859e2239f17f5c661ab38540aa9","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"7ca87883b030f4a0dd549f0a60987655f536528328bcee5c9e46b607470827b5","device":"13603a08ada71730","findings":[],"hash":"031c40f32b6cf1c8ffbadc9e7bbdd8d4b8ad2dc891a88b1cd1d30951834339e3","nodeId":"fa6feb51-a1d9-4f4c-a51f-3b436f4621ad","outcome":"completed","summary":"I fixed the medium finding. The reviewer's proof now passes, and `forge build`, `forge test` (51 passed with the job's fuzz seed) and `forge fmt --check` all pass.\n\n**Hedge capture (medium) — fixed.** I ran the proof on the starting tree first and it failed as reported: a bettor staked 1 wei on each of 3 hamsters, got all 3 wei back, took the whole 1,000 HAMSTER budget, and the sponsor could reclaim nothing.\n\nWhat changed in `src/HamsterRaces.sol`:\n- **Smaller loser pool.** When a result is reported, losers now share at most budget × (losing ETH ÷ total ETH). That works out to at most budget ÷ total ETH per losing wei, for a hedger and an honest loser alike. Covering every hamster can no longer take the whole budget.\n- **The rest goes back to the sponsor.** The winning side's share of the budget can be reclaimed by the race's sponsor once the result window ends (`resolveBy`). Cancelled races and races with no losers can still be reclaimed in full straight away.\n- **Unclaimed loser rewards are protected.** The sponsor can never reclaim HAMSTER that losers have not yet claimed.\n\nOne of my new tests caught a bug I had introduced along the way: `reclaimRewards` was still setting the remaining rewards to zero instead of subtracting the amount paid. I fixed it.\n\nThe fix limits the hedge but does not remove it. The only way to make covering every hamster cost ETH is a fee on the losing pool, and the brief gives no fee rate, which is the requester's to set. So a lone hedger with equal stakes on N hamsters still takes (N−1)/N of the budget at no ETH cost. In a race with other bettors, a large hedge still dilutes honest losers. The README now says this plainly and tells sponsors to size budgets on the assumption that anyone can take that share risk-free.\n\n**Tests.** I added `test/HamsterRacesHedge.t.sol`, which covers:\n- the lone hedger;\n- the reclaim timing, one second before and exactly at `resolveBy`;\n- a sponsor reclaim before losers have claimed;\n- a fuzz test bounding what a hedge can earn.\n\nThe invariant test now checks the loser-pool formula and that unclaimed loser rewards are never more than the race's remaining rewards. I updated the existing tests to the new amounts.\n\n**Reporter trust model (info) — disputed, no code change.** It reproduces as described, but a trusted reporter is the documented design and the brief asks for no owner veto or reporter exclusion. I added README notes that the reporter and sponsor can bet, that the owner cannot cancel an open race, and that the owner must set `closesAt` before the real race starts.\n\n**Vendored-dependency note (info) — fixed.** The two OpenZeppelin files differ from upstream only in whitespace. `lib/DEPENDENCIES.md` now says they were reformatted by `forge fmt` rather than claiming they are unmodified.\n\nAll three answers are in `.imd-responses.json`. I also updated `SECURITY.md` to match. Nothing is committed.","treeHash":"41b8a0939fd5c6a30406ed122dd44cec5793a371","usage":{"cachedInputTokens":904887,"inputTokens":26,"model":"claude-opus-5-5","outputTokens":21694,"runtime":"claude","turns":15,"wallClockMs":281701}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"5ded77c3c883b7b0","findings":[{"citation":"resolved","description":"Economic Security / Invariant: the only stated purpose of the application is 'bet on hamster, lose to earn $HAMSTER'. The loser reward is paid pro rata to losing ETH stake (rewardBudget * s / L) while the ETH pot is paid pro rata to winning stake (totalStaked * s / W). Nothing couples a bettor's HAMSTER reward to ETH it actually loses, and the one-bet-per-address rule in placeBet (src/HamsterRaces.sol:183) is per address, so one actor using one address per hamster (at most 16) holds every outcome. Its winning address recovers T*x/W of the pot, which when it is the only bettor equals its entire outlay, and its losing addresses receive (N-1)x/L of the budget, which is 100% when it is alone and asymptotically 100% as x grows. Consequences: (1) any race that attracts no genuine bettors loses its full reward budget to a griefer for N wei plus gas instead of returning it to the sponsor (reclaimRewards reverts NoRefundableRewards because the race is Settled with L > 0); (2) in a race with honest bettors a capitalised hedger pays at most N*O_w - O ETH (bounded by the honest stakes) to take almost all of R, and every honest loser's HAMSTER, the product the brief promises, is diluted toward zero. The README acknowledges 'deliberate losing bets' as a sponsor concern, but not that the hedge is riskless and captures 100% of the budget, and the owner itself can use the same move to silently renege on the advertised rewards without any privileged call. Invariant broken: 'HAMSTER paid to a party is bounded by ETH that party actually loses'. Fix needs a design decision, since the contract cannot see that N addresses are one actor: make the hedge cost real by routing losing ETH (or a fixed share of it) to the sponsor/treasury instead of to the winning side, or cap each loser's reward at a HAMSTER-per-ETH rate fixed at createRace with the unused budget reclaimable, or require a minimum stake and a maximum reward per address. Any of these preserves the 'lose to earn' rule but bounds extraction by ETH genuinely lost.","line":247,"path":"src/HamsterRaces.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {HamsterToken} from \"src/HamsterToken.sol\";\nimport {HamsterRaces} from \"src/HamsterRaces.sol\";\n\n/// @dev \"Lose to earn\": a bettor who covers every hamster from one address each bears no race risk,\n///      recovers every wei it staked through its winning address, and the loss-reward formula still\n///      pays it the sponsor's entire HAMSTER budget. Fails on the current code: the hedge costs no\n///      ETH and takes the whole budget. Passes once either leg is fixed (the hedge costs ETH, or a\n///      no-risk hedge cannot take the whole budget).\ncontract HedgeCaptureTest is Test {\n    HamsterToken token;\n    HamsterRaces races;\n    address owner = makeAddr(\"owner\");\n    address reporter = makeAddr(\"reporter\");\n    uint256 constant REWARD = 1_000 ether;\n    uint64 closesAt;\n    uint256 raceId;\n\n    function setUp() public {\n        vm.warp(1_000_000);\n        token = new HamsterToken();\n        races = new HamsterRaces(token, owner);\n        token.transfer(owner, REWARD * 10);\n        vm.prank(owner);\n        races.setResultReporter(reporter);\n        closesAt = uint64(block.timestamp + 1 hours);\n        vm.startPrank(owner);\n        token.approve(address(races), REWARD);\n        raceId = races.createRace(keccak256(\"race\"), 3, closesAt, REWARD);\n        vm.stopPrank();\n    }\n\n    function test_riskFreeHedgeTakesTheWholeRewardBudget() public {\n        address[3] memory sybil = [makeAddr(\"s1\"), makeAddr(\"s2\"), makeAddr(\"s3\")];\n        uint256 staked;\n        for (uint8 i; i < 3; ++i) {\n            vm.deal(sybil[i], 1);\n            vm.prank(sybil[i]);\n            races.placeBet{value: 1}(raceId, i + 1);\n            staked += 1;\n        }\n        vm.warp(closesAt);\n        vm.prank(reporter);\n        races.reportResult(raceId, 2);\n\n        uint256 ethBack;\n        uint256 hamsterGot;\n        for (uint8 i; i < 3; ++i) {\n            vm.prank(sybil[i]);\n            (uint256 e, uint256 h) = races.claim(raceId, payable(sybil[i]));\n            ethBack += e;\n            hamsterGot += h;\n        }\n        emit log_named_uint(\"eth staked by hedger\", staked);\n        emit log_named_uint(\"eth returned to hedger\", ethBack);\n        emit log_named_uint(\"hamster paid to hedger\", hamsterGot);\n        emit log_named_uint(\"hamster left for sponsor\", races.getRace(raceId).rewardsRemaining);\n\n        // Sponsor cannot recover anything: the race is Settled with losing stake present.\n        vm.prank(owner);\n        vm.expectRevert(HamsterRaces.NoRefundableRewards.selector);\n        races.reclaimRewards(raceId, owner);\n\n        assertTrue(\n            ethBack < staked || hamsterGot < REWARD,\n            \"a hedge that cost no ETH captured the sponsor's entire HAMSTER reward budget\"\n        );\n    }\n}","reproduction":"State: owner called setResultReporter(reporter) and createRace(ref, 3, now+1h, 1000e18) after approving 1000 HAMSTER. Scenario A (no other bettors): addresses s1,s2,s3 each call placeBet{value:1 wei}(raceId, 1|2|3). warp to closesAt; reporter calls reportResult(raceId, 2). s2 claims: receives ethAmount = 3 wei (totalStaked*1/1). s1 and s3 claim: hamsterAmount = 1000e18*1/2 each, total 1000e18. Owner calls reclaimRewards(raceId, owner): reverts NoRefundableRewards. Expected: a bettor set with zero net ETH loss earns no reward, or the sponsor can recover the budget; actual: the hedger has all 3 wei back and all 1000 HAMSTER, sponsor has 0. Scenario B (honest bettors present): alice bets 1 ETH on 1, bob bets 1 ETH on 2, hedger addresses bet 100 ETH on each of 1,2,3. Winner 1. Measured with forge: hedger staked 300 ETH, received 299.0099 ETH back and 995.0249 HAMSTER; bob lost 1 ETH and received 4.9751 HAMSTER (0.5% of the budget); alice received 2.9901 ETH. The hedger paid 0.99 ETH (to alice) for 99.5% of the sponsor's budget. Run test/scratch/HedgeCapture.t.sol: forge test --match-path test/scratch/HedgeCapture.t.sol (fails on current code).","severity":"medium","snippet":"            hamsterAmount = bet.amount == race.loserStakeRemaining\n                ? race.rewardsRemaining\n                : Math.mulDiv(race.rewardBudget, bet.amount, race.totalStaked - stakes[raceId][race.winner]);","title":"Loss-reward formula lets a bettor covering every hamster take the whole HAMSTER budget with no ETH at risk, diluting honest losers to dust"}],"hash":"06a3c6f5c868ac0de01c7551849234bf54c2cf8c1dbad1b9bb67a896b08ee6ff","nodeId":"8cdb841b-57ba-4967-9067-bc1ef8e4f54a","outcome":"completed","summary":"Review complete. The findings file is at `.imd-findings.json` with one substantiated finding and a full coverage record. Nothing outside `.imd-findings.json` and `test/scratch/` was changed, and `git status` shows the tree clean apart from those untracked outputs.\n\n**What I found**\n\nOne medium finding, in the economics of `HamsterRaces.claim`. The loser reward is paid pro rata to losing ETH stake, and the ETH pot pro rata to winning stake, so nothing ties a party's HAMSTER reward to ETH it actually loses. One actor using one address per hamster covers every outcome. Verified with a Foundry test on the current code:\n\n| Scenario | Hedger ETH staked | ETH returned | HAMSTER taken | Honest loser's HAMSTER |\n|---|---|---|---|---|\n| No other bettors, 1 wei per hamster | 3 wei | 3 wei | 1,000 (100%) | n/a, sponsor reclaim reverts |\n| Alice and Bob 1 ETH each, hedger 100 ETH per hamster | 300 ETH | 299.01 ETH | 995.02 (99.5%) | 4.98 (0.5%) |\n\nThe README acknowledges deliberate losing bets as a sponsor concern, but not that the hedge is riskless and takes the entire budget. The fix needs a design decision, since the contract cannot see that several addresses are one actor. Options that keep the \"lose to earn\" rule: route losing ETH, or a share of it, to the sponsor instead of the winning side; cap each loser's reward at a HAMSTER-per-ETH rate fixed at race creation with the unused budget reclaimable; or add minimum stake and per-address reward limits. The attached proof asserts only that a hedge which costs no ETH cannot take the whole budget, so it passes under any of those fixes.\n\n**What held**\n\nEverything else in my area traced clean: ETH and HAMSTER liabilities match the per-race remainders and are always covered, winner payouts sum exactly to the pot and loser rewards to the budget with dust to the last claimant in each group, settlement and betting windows are disjoint, refunds are exact stakes, sponsor reclaim cannot touch owed rewards, and ownership changes move no escrow. The token is unmodified OpenZeppelin ERC-20 with a fixed constructor mint, so launch flows arrive whole. The Slither reentrancy lead on `createRace` is owner-only, guarded and against the trusted HAMSTER token; the missing-zero-check lead is a false positive since `_checkRecipient` rejects zero. The existing 47 tests pass.\n\n**Coverage record**\n\nAll 13 entry points have rows, plus three invariant rows. `claim` and `placeBet` point at finding 1; the rest are `holds`. Nothing is marked `unreached`.","treeHash":null,"usage":{"cachedInputTokens":888069,"inputTokens":354,"model":"claude-fable-5-1","outputTokens":27385,"runtime":"claude","turns":27,"wallClockMs":433548}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"d2d0460994f47014","findings":[],"hash":"2e1040e8d18911da74a77bc0965febf06f5cc1084863131184742f0f0b537c33","nodeId":"031838c3-4df6-485e-b418-f20a507d586f","outcome":"completed","summary":"The review is complete. The findings file holds an empty findings list and a full coverage record, and the working tree is clean apart from that file.\n\n**Result: no defects in the math, boundary or numerical-gap area.**\n\nWhat I checked and how:\n\n- **Pari-mutuel distribution in `claim`.** Winners get floor(T·s/W), losers get floor(R·s/L), and the last claimant in each group takes that group's remainder. A 2000-run fuzz over 8 bettors, stakes from 1 wei to 1e6 ETH, budgets from 1 unit to the full 1e27 supply, and random claim order confirmed exact floor shares for non-last claimants, whole-pot and whole-budget conservation, and both global liabilities draining to zero.\n- **Division safety.** The winner divisor is nonzero because a zero winning stake cancels the race instead of settling it. The loser divisor is nonzero because a loser's own stake is part of it. Neither `mulDiv` result can exceed its numerator bound, so no 256-bit overflow.\n- **Zero-rounding.** Winner shares never round to zero since T ≥ W. Loser shares can round to zero for a non-last claimant when R·s < L, for example budget 1 with two 1 ETH losers: the first gets 0, the last gets 1. The README documents this as intended and nothing is stranded, so I recorded it as a coverage note rather than a finding.\n- **Deadline boundaries.** Creation accepts closesAt from now+1 through now+7 days and rejects both outer edges. Betting stops exactly at closesAt, reporting opens exactly there, and at resolveBy reporting is refused while public cancellation opens, with no overlap or gap.\n- **The L==0 and W==0 branches.** All-winners races pay each winner exactly their stake and let the sponsor reclaim the full budget. No-winner races cancel with exact refunds and full reclaim.\n- **Static-analysis leads.** The slither reentrancy line on `createRace` is a false positive: the function is non-reentrant, the token is unmodified OpenZeppelin 5.0.2, and state is written before the pull. The missing zero-check lead is covered by the recipient helper. The timestamp leads are the intended deadline comparisons.\n\nOut of my area, the ownership and token entry points are unmodified OpenZeppelin code with nothing arithmetic to check, and I marked them as holding with that reason.","treeHash":null,"usage":{"cachedInputTokens":817713,"inputTokens":354,"model":"claude-fable-5-1","outputTokens":21413,"runtime":"claude","turns":20,"wallClockMs":436046}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"d4b53d656a3fbe07","findings":[{"citation":"resolved","description":"Merged from write_foundry_tests (medium), audit_economics (medium) and audit_flow (info); same root cause. The loser reward is rewardBudget * s / L per losing address and the ETH pot is totalStaked * s / W per winning address, with no application fee and no link between a HAMSTER reward and ETH actually lost. placeBet (src/HamsterRaces.sol:183) limits one bet per address, not per person, so one actor using hamsterCount addresses holds every outcome. When it is the only bettor its winning address receives totalStaked * s / W = all of its ETH back and its losing addresses receive 100% of rewardBudget; reclaimRewards then reverts NoRefundableRewards (src/HamsterRaces.sol:271-272) because the race is Settled with L > 0, so the sponsor recovers nothing. With honest bettors present a capitalised hedger staking x on every hamster pays at most the honest winners' pro-rata share (bounded by honest stakes) and takes (N-1)x / L of the budget, which approaches 100% as x grows, diluting every honest loser's HAMSTER toward zero. Recalibrated to medium: no theft of a bettor's funds and no permanent breakage, but a voluntary sponsor budget is lost under a condition anyone can create for N wei plus gas, and the product's stated purpose ('lose to earn') is defeated for honest losers whenever a hedger is present. README.md lines 204-207 say users may bet on several hamsters from several addresses and that the sponsor must account for it, but not that the hedge is risk-free and captures the entire budget, and the sponsor has no way to recover a budget captured this way. The fix is a design decision the author must make, since the contract cannot see that N addresses are one actor; any of these preserves 'lose to earn' while bounding extraction by ETH genuinely lost: (a) route losing ETH, or a fixed share of it, to the sponsor instead of to the winning side so the hedge has a cost; (b) cap each loser's reward at a HAMSTER-per-ETH rate fixed at createRace with the unused budget reclaimable by the sponsor; (c) require a minimum stake and a maximum reward per address. If the requester instead accepts the property as designed, README.md should state plainly that the hedge is risk-free, that the full budget can be taken by one actor at no cost in a race with no other bettors, and that the sponsor cannot reclaim it.","line":247,"path":"src/HamsterRaces.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {HamsterToken} from \"src/HamsterToken.sol\";\nimport {HamsterRaces} from \"src/HamsterRaces.sol\";\n\n/// @dev \"Lose to earn\": a bettor who covers every hamster from one address each bears no race risk,\n///      recovers every wei it staked through its winning address, and the loss-reward formula still\n///      pays it the sponsor's entire HAMSTER budget. Fails on the current code: the hedge costs no\n///      ETH and takes the whole budget. Passes once either leg is fixed (the hedge costs ETH, or a\n///      no-risk hedge cannot take the whole budget).\ncontract HedgeCaptureTest is Test {\n    HamsterToken token;\n    HamsterRaces races;\n    address owner = makeAddr(\"owner\");\n    address reporter = makeAddr(\"reporter\");\n    uint256 constant REWARD = 1_000 ether;\n    uint64 closesAt;\n    uint256 raceId;\n\n    function setUp() public {\n        vm.warp(1_000_000);\n        token = new HamsterToken();\n        races = new HamsterRaces(token, owner);\n        token.transfer(owner, REWARD * 10);\n        vm.prank(owner);\n        races.setResultReporter(reporter);\n        closesAt = uint64(block.timestamp + 1 hours);\n        vm.startPrank(owner);\n        token.approve(address(races), REWARD);\n        raceId = races.createRace(keccak256(\"race\"), 3, closesAt, REWARD);\n        vm.stopPrank();\n    }\n\n    function test_riskFreeHedgeTakesTheWholeRewardBudget() public {\n        address[3] memory sybil = [makeAddr(\"s1\"), makeAddr(\"s2\"), makeAddr(\"s3\")];\n        uint256 staked;\n        for (uint8 i; i < 3; ++i) {\n            vm.deal(sybil[i], 1);\n            vm.prank(sybil[i]);\n            races.placeBet{value: 1}(raceId, i + 1);\n            staked += 1;\n        }\n        vm.warp(closesAt);\n        vm.prank(reporter);\n        races.reportResult(raceId, 2);\n\n        uint256 ethBack;\n        uint256 hamsterGot;\n        for (uint8 i; i < 3; ++i) {\n            vm.prank(sybil[i]);\n            (uint256 e, uint256 h) = races.claim(raceId, payable(sybil[i]));\n            ethBack += e;\n            hamsterGot += h;\n        }\n        emit log_named_uint(\"eth staked by hedger\", staked);\n        emit log_named_uint(\"eth returned to hedger\", ethBack);\n        emit log_named_uint(\"hamster paid to hedger\", hamsterGot);\n        emit log_named_uint(\"hamster left for sponsor\", races.getRace(raceId).rewardsRemaining);\n\n        // Sponsor cannot recover anything: the race is Settled with losing stake present.\n        vm.prank(owner);\n        vm.expectRevert(HamsterRaces.NoRefundableRewards.selector);\n        races.reclaimRewards(raceId, owner);\n\n        assertTrue(\n            ethBack < staked || hamsterGot < REWARD,\n            \"a hedge that cost no ETH captured the sponsor's entire HAMSTER reward budget\"\n        );\n    }\n}","reproduction":"State: owner calls setResultReporter(reporter); owner approves 1000e18 HAMSTER and calls createRace(keccak256('race'), 3, now + 1 hours, 1000e18). Addresses s1, s2, s3 (one actor) each call placeBet{value: 1 wei}(raceId, 1), (raceId, 2), (raceId, 3). vm.warp(closesAt); reporter calls reportResult(raceId, 2). s1, s2, s3 each call claim(raceId, self). Expected: a party with zero net ETH loss earns no loss reward, or the sponsor can recover the budget. Actual (forge logs from the attached test): eth staked 3, eth returned 3, hamster paid 1000000000000000000000, hamster left for sponsor 0; owner's reclaimRewards(raceId, owner) reverts NoRefundableRewards. Second scenario from audit_economics, reproduced by tracing the formulas: alice 1 ETH on 1, bob 1 ETH on 2, hedger 100 ETH on each of 1, 2, 3; winner 1 (T = 302 ETH, W = 101 ETH, L = 201 ETH): hedger's winning address gets 302 * 100 / 101 = 299.0099 ETH and its two losing addresses get 200 * 1000e18 / 201 = 995.02e18 HAMSTER, so it paid 0.99 ETH for 99.5% of the budget; bob loses 1 ETH and gets 1000e18/201 = 4.975e18 HAMSTER (0.5% of the budget). Run: forge test --match-path test/scratch/HedgeCapture.t.sol (fails on this code with 'a hedge that cost no ETH captured the sponsor's entire HAMSTER reward budget'; both specialist proofs were run and fail the same way).","severity":"medium","snippet":"            hamsterAmount = bet.amount == race.loserStakeRemaining\n                ? race.rewardsRemaining\n                : Math.mulDiv(race.rewardBudget, bet.amount, race.totalStaked - stakes[raceId][race.winner]);","title":"Loss-reward formula pays the whole HAMSTER budget to a bettor who covers every hamster and so loses no ETH; the sponsor cannot reclaim it"},{"citation":"resolved","description":"Merged from audit_permissions (info) and write_foundry_tests' low 'bets stay open until closesAt'. Not a permission bypass; recorded so the launch owner can weigh the privileged power. reportResult and pre-timeout cancelRace are gated only on race.reporter, snapshotted at createRace from resultReporter. placeBet (src/HamsterRaces.sol:178-191) does not exclude race.reporter or race.sponsor, so the reporter can hold a position in the race it settles and report in its own favour. setResultReporter changes only future races and there is no owner-side cancel, so a compromised reporter key after a race opens can only be overridden by the public timeout at resolveBy. placeBet accepts entries while block.timestamp < closesAt (src/HamsterRaces.sol:180), which the owner sets independently of the external event, so if the physical race ends before closesAt anyone who sees the result can bet on the known winner. All of this is documented in README.md (lines 21-27, 111-112, 197-202) as the trust model, and no unprivileged amplifier exists: the reporter's result cannot be submitted before closesAt and no bettor can react to it. Mitigations (owner emergency cancel, excluding reporter and sponsor from placeBet, or an on-chain event-start time) would be design changes; none is required by the brief.","line":198,"path":"src/HamsterRaces.sol","reproduction":"State: owner calls setResultReporter(R); approves and calls createRace(ref, 3, now + 1 hours, 1000e18). Alice calls placeBet{value: 10 ether}(id, 1). R calls placeBet{value: 1}(id, 2): accepted (no reporter exclusion). vm.warp(closesAt); R calls reportResult(id, 2): accepted since stakes[id][2] == 1 wei; race Settled. R calls claim(id, R): receives ethRemaining = 10 ether + 1 wei; Alice's claim pays 1000e18 HAMSTER. Owner calls cancelRace(id) before resolveBy on another open race: reverts CancellationNotAllowed. Expected under an honest reporter: the externally observed winner is reported and entry closes before the event. Actual: the outcome is fully at R's discretion and the contract cannot enforce the event schedule.","severity":"info","snippet":"        if (msg.sender != race.reporter) revert NotReporter();","title":"Trust assumptions: the per-race reporter alone decides the outcome, may itself bet, the owner has no veto over an open race, and entry stays open until closesAt regardless of when the external race ru"},{"citation":"resolved","description":"Kept from audit_flow (info) after reproducing it. The recorded archive SHA-256 (18c7b7e9...925a) matches the upstream v5.0.2 tarball, and ERC20.sol, Math.sol, SafeERC20.sol, ReentrancyGuard.sol, Ownable.sol and Ownable2Step.sol are byte-identical to upstream. utils/Address.sol and token/ERC20/extensions/IERC20Permit.sol are not: the differences are a forge fmt pass (line 46 '(bool success, )' became '(bool success,)'; lines 114-118 reflowed a function signature). After stripping all whitespace both files hash identically to upstream, so semantics are unchanged and nothing the application relies on is affected. The provenance sentence is nevertheless false as written, which matters to anyone verifying the vendored closure by file hash. Fix: restore the upstream bytes for those files, or change the sentence to say the files were reformatted with forge fmt. No change to src/ is needed.","line":13,"path":"lib/DEPENDENCIES.md","reproduction":"From the repository root: curl -sSL -o oz.tgz https://codeload.github.com/OpenZeppelin/openzeppelin-contracts/tar.gz/refs/tags/v5.0.2 (sha256 18c7b7e949b9a82dcd8cd394426c9c2636dfc263aa2317d4749dbfa0c7b3925a, matching DEPENDENCIES.md), tar xzf oz.tgz, then cmp openzeppelin-contracts-5.0.2/contracts/utils/Address.sol lib/openzeppelin-contracts/contracts/utils/Address.sol. Expected (per DEPENDENCIES.md line 13): identical. Actual: differ at line 46 and lines 114-118; same for token/ERC20/extensions/IERC20Permit.sol. tr -d ' \\t\\n\\r' on both copies gives identical sha256 (bde0d2e9bd1db985... for Address.sol), confirming the difference is whitespace only.","severity":"info","snippet":"OpenZeppelin contains only the required source dependency closure and its MIT license. Forge Std contains its src tree and both license files. Sources are unmodified. All dependencies are ordinary files; no submodules or network downloads are needed to build.","title":"lib/DEPENDENCIES.md says vendored sources are unmodified, but Address.sol and IERC20Permit.sol differ from OpenZeppelin v5.0.2 by formatting"}],"hash":"3fbc97d2d9ccb1eb5a617049eeb9dbe444d0920b1c3ee61d7023d280747422b6","nodeId":"9cc22519-7bfb-4313-aa3f-39f1c2f51628","outcome":"completed","summary":"The review is complete and `.imd-findings.json` holds three findings plus a coverage record for all thirteen entry points. The scratch directory is removed and the tree is otherwise untouched.\n\n**What I kept**\n\n- **Medium, risk-free hedge takes the HAMSTER budget.** Three specialists reported the same root cause and I merged them. One actor betting from one address per hamster recovers all of its ETH through the winning address and collects the entire loser reward through the others. The sponsor cannot reclaim because the race is Settled with losing stake present. Both specialist proofs fail on the current code for exactly that reason, and the proof I attached is the one that logs 3 wei staked, 3 wei returned, and the full 1000 HAMSTER paid. I set it to medium rather than the specialists' split of medium and info: no bettor loses funds, but a voluntary budget is lost under a condition anyone can create for a few wei, and honest losers' rewards dilute toward zero whenever a hedger is present. The fix is a design decision, so I listed the options and the documentation-only alternative.\n- **Info, trust assumptions.** The reporter alone decides outcomes, may itself bet, and the owner has no veto over an open race. Entry stays open until closesAt whatever the external race does. I merged audit_permissions' note with write_foundry_tests' low on closesAt since both are the same documented trust model, and traced each call path.\n- **Info, DEPENDENCIES.md provenance.** I downloaded the OpenZeppelin v5.0.2 tarball, confirmed its digest matches the record, and found `Address.sol` and `IERC20Permit.sol` differ by formatting only. Whitespace-stripped hashes match upstream, so nothing semantic changed, but the sentence claiming unmodified sources is false.\n\n**What I dropped**\n\n- Locked donations to the contract: the README states this is deliberate to avoid a general admin withdrawal power, so it is a design choice, not a defect.\n- Every slither and aderyn line: the reentrancy lead in createRace is unreachable with the fixed OpenZeppelin token and nonReentrant guard, the zero-check exists, and timestamps gate deadlines only.\n\n**My own pass** found nothing beyond the specialists. Share arithmetic cannot underflow the liabilities, placeBet and reportResult are mutually exclusive by timestamp so there is no same-block front-run, the token is byte-identical upstream ERC20, and the manifest's constructor arguments match both constructors with the requester as owner. The project's own 53 tests pass with the pinned fuzz seed.","treeHash":null,"usage":{"cachedInputTokens":462164,"inputTokens":194,"model":"claude-fable-5-1","outputTokens":18062,"runtime":"claude","turns":18,"wallClockMs":297507}},{"artifacts":[],"attempt":1,"bundleHash":"6aefbe9ddf448f6ee75f6ac5c4b3787254e07dcbbe07e43ee677fbe403ce3599","device":"99c6d0bcc495ad61","findings":[],"hash":"57e3e16f7e6cf1c9cce2f17d0ae7bfe3d34fe6c50dae1d67c158e03374387695","nodeId":"dde76baf-8b49-4dd5-8721-5abb8e0807aa","outcome":"completed","summary":"Done. I wrote launch.json for the HAMSTER custom token launch, and it is the only file changed.\n\n**What the manifest says**\n\n- **Token** is HamsterToken, name and symbol HAMSTER, 18 decimals, no constructor arguments. Its totalSupply is one billion tokens in minor units, exactly what the constructor mints once to msg.sender.\n- **One application contract**, HamsterRaces, with constructorArgs `$token` and `$requester`, matching its constructor signature of the HAMSTER token and an owner. The owner is the economics remainder recipient, so neither the factory nor IMD owns it, which satisfies the protected owner check.\n- **Pool and economics** are copied verbatim from the launch terms: IMD pair address, fee 12500, tick spacing 60, the one-to-one provenance price, and the requester's economics object.\n- **Notes** describe only the two contracts in the accepted tree and the post-launch setResultReporter step. They are 1246 characters, within the limit.\n\n**Checks**\n\n- A scripted check confirmed the manifest parses, uses only the schema's six top-level keys, carries the exact supply and the verbatim economics.\n- `forge build` succeeds. `forge test` with the verifier's fuzz seed passed all 47 tests across 5 suites.\n- `git status` shows launch.json as the sole new file. No configuration, lib, or source file was touched.","treeHash":"c3c4ee9a364a238cb836b317595b4e9b105c715b","usage":{"cachedInputTokens":209581,"inputTokens":130,"model":"claude-fable-5-1","outputTokens":2987,"runtime":"claude","turns":9,"wallClockMs":60897}},{"artifacts":[],"attempt":1,"bundleHash":"47b9f70fe648800effad57dbda8349aa0e65389da7b245a3a47b98d76babab73","device":"fdbedee33322a6ef","findings":[],"hash":"71a07757f9e3812e709722585dc722f1ec8f2c1ed3f3c93adb4ef4b2b7c238a8","nodeId":"5183ade7-40ab-41ae-ac6b-dc1a998dc8f4","outcome":"completed","summary":"I updated my two test files from last round to match the revised reward rules, and the full suite now passes: `forge build` is clean and `forge test` with the verifier's seed runs 57 of 57 tests in 8 suites.\n\n**Why they were failing.** The implementer changed `src/HamsterRaces.sol` after my last round. Losers now share only part of the reward budget: a pool of `budget × losing stake ÷ total stake`, rounded down. The sponsor can take back the rest once the result deadline (`resolveBy`) has passed. This is a deliberate change and it is written up in the README, so it isn't a defect. My tests still expected losers to split the whole budget, which made 4 invariants and 2 fuzz tests fail. I changed only what the new rules required.\n\n**`test/HamsterRacesLedgerInvariant.t.sol`**\n- The handler's reclaim step and its \"this call must fail\" check now follow the new rule:\n  - the race is finished;\n  - for a settled race with a non-zero loser pool, the deadline has passed;\n  - something is left beyond what losers are still owed.\n- It also flags any reclaim that cuts into rewards still owed to losers, or that returns a different amount than the unowed remainder.\n- New invariants for each race:\n  - a settled race's loser pool equals the formula;\n  - the pool still owed plus what losers have been paid equals the pool;\n  - the sponsor never takes more than the budget minus the pool;\n  - nothing is reclaimed early while losers have a pool;\n  - a race that was cancelled or is still open has no pool and pays no loss rewards.\n- The per-bet check now measures each loser's share against the pool. It also checks the cap: no loser gets more than `budget ÷ total stake` per losing wei, give or take rounding.\n\n**`test/HamsterRacesPayoutFuzz.t.sol`**\n- Whatever order people claim in, losers now split exactly the pool.\n- The sponsor's reclaim fails one second before the deadline, works at the deadline for exactly the rest of the budget, and fails if tried again. Afterwards the contract holds no ETH or HAMSTER.\n- The tiny-budget tests (1, 2 and 7 minor units, plus a large budget) now cover pools that round down to zero. In that case the sponsor can reclaim straight away.\n- New unit test: with a 1-unit budget, the loser is paid 0 and the sponsor gets the 1 unit back immediately.\n\nTo make sure the new invariants aren't passing without testing anything, I drove the handler step by step through one sequence in a scratch test. The sponsor's share was locked before the deadline, came back at the expected amount after it, the loser was paid the pool, and nothing was flagged.\n\n**Findings:** I found no new defect, so I did not write `.imd-findings.json`. One risk remains in the design, and the README already states it: someone who bets on every hamster from separate addresses risks no ETH. With equal stakes on N hamsters and no other bettors, they still collect `(N−1)/N` of the reward budget. The new cap limits this but doesn't remove it.","treeHash":"d59cb5812d8e5f935d0ceac8692595905a999e85","usage":{"cachedInputTokens":728466,"inputTokens":26,"model":"claude-opus-5-5","outputTokens":13074,"runtime":"claude","turns":16,"wallClockMs":472660}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"b273d407784470b4","findings":[{"citation":"resolved","description":"The one-bet-per-address rule limits addresses, not people. In a race where one actor is the only participant and stakes equal amounts on every hamster from hamsterCount addresses, the pari-mutuel winner payout T*s/W returns exactly the full ETH staked (T = n*s, W = s) and the losing addresses collect 100% of rewardBudget. The reward budget is therefore extractable for gas cost alone whenever a race has little or no honest participation. With honest bettors present the actor's ETH exposure becomes zero-sum against them, so this is an economic property of 'lose to earn' rather than a code bug. README.md already states it ('One address is not one person ... The sponsor must account for that incentive when funding races'). Reported so the judge and sponsor have a concrete reproduction and sizing; no code change is proposed because any fix (identity checks, minimum honest participation, caps) would change the agreed design.","line":183,"path":"src/HamsterRaces.sol","reproduction":"Owner: setResultReporter(R); approve 1000e18 HAMSTER; createRace(ref, 3, now+1h, 1000e18). Addresses s1,s2,s3 (one actor) each placeBet{value: 1 ether}(id, 1/2/3). warp(closesAt); R: reportResult(id, 2). s1,s2,s3 each claim(id, self). Observed: ETH returned = 3 ether (all of it), HAMSTER paid = 1000e18 (whole budget). Expected under the stated design: the same; the sponsor's budget is simply awarded to whoever supplies losing stake. Scratch test test/scratch/Sybil.t.sol asserted exactly these totals and passed.","severity":"info","snippet":"        if (bets[raceId][msg.sender].amount != 0) revert AlreadyBet();","title":"One actor covering every hamster from several addresses recovers all ETH and takes the entire HAMSTER budget risk-free (documented economic property, not a code defect)"},{"citation":"resolved","description":"Periphery check: I fetched the recorded archives (OpenZeppelin v5.0.2, SHA-256 18c7b7e9... matches the file's record; forge-std v1.9.7) and compared every vendored file byte for byte. openzeppelin-contracts/contracts/utils/Address.sol, token/ERC20/extensions/IERC20Permit.sol and seven forge-std files (StdJson, StdAssertions, Vm, StdToml, console, interfaces/IERC7540, interfaces/IMulticall3) differ from upstream. Every difference is whitespace or line-wrapping (a forge fmt pass); after stripping all whitespace every .sol file hashes identically to upstream, so the semantics, including Math.mulDiv, SafeERC20, ReentrancyGuard, Ownable2Step and ERC20, are the genuine releases. The provenance statement is nevertheless false as written, which matters for anyone verifying the vendored closure by file hash rather than by reading. Fix: either restore the upstream bytes for those files or change the sentence to say the files were reformatted with forge fmt.","line":13,"path":"lib/DEPENDENCIES.md","reproduction":"From the repository root: download https://codeload.github.com/OpenZeppelin/openzeppelin-contracts/tar.gz/refs/tags/v5.0.2, extract, and run `cmp lib/openzeppelin-contracts/contracts/utils/Address.sol <extracted>/contracts/utils/Address.sol`. Expected (per DEPENDENCIES.md): identical. Actual: differ (line 46 `(bool success, )` vs `(bool success,)`; lines 114-118 signature reflowed). Same for IERC20Permit.sol lines 66-74 and the forge-std files listed.","severity":"info","snippet":"OpenZeppelin contains only the required source dependency closure and its MIT license. Forge Std contains its src tree and both license files. Sources are unmodified. All dependencies are ordinary files; no submodules or network downloads are needed to build.","title":"lib/DEPENDENCIES.md states vendored sources are unmodified, but several files differ from the recorded releases by formatting"}],"hash":"72dd8197d262b3ed219720c37ae6c33c6f08350a53e221bc03104346accdf719","nodeId":"6beca9ff-1b4e-4ad2-87a5-6808e3359012","outcome":"completed","summary":"The findings file is in place and the tree is otherwise untouched. Review complete.\n\n**Outcome:** no exploitable defect found in HamsterToken or HamsterRaces within the Execution Trace, Periphery and First Principles areas. The findings file holds two informational notes and a full coverage record.\n\n**What I checked**\n\n- **Execution trace.** Every entry point traced from guard to final state. The claim function's three branches were walked with concrete values. The last-in-group test (stake equals remaining stake) is exact because every stake is positive, so floor sums never exceed the remaining ETH or reward pool. Deadlines were probed at the exact boundaries, including the uint64 overflow guard at the maximum close time. The reporter snapshot means a mid-race reporter change cannot affect an open race.\n- **Periphery.** I fetched the recorded OpenZeppelin v5.0.2 and forge-std v1.9.7 releases and compared every vendored file. The archive hash matches the record. All differences are formatter whitespace only. Stripping whitespace makes every Solidity file hash identical to upstream, so the inherited ERC20, Ownable2Step, SafeERC20, ReentrancyGuard and mulDiv code is genuine.\n- **First principles.** Settled races always have a positive winning stake, so no zero denominator is reachable. Cancellation only flips status and refunds reconcile to the total staked. Compiled artifacts have no unlinked libraries and no forbidden opcodes.\n- **Static-analysis leads.** The Slither reentrancy lead on createRace is rejected: the token is a plain OpenZeppelin ERC20 with no hooks, the function is nonReentrant, and a revert rolls back the liability. The timestamp leads are intended deadline checks.\n\n**Findings recorded**\n\n1. **Info:** a single actor covering every hamster from several addresses recovers all ETH and takes the entire reward budget. Reproduced in a scratch test. The README already documents this as the sponsor's accepted economic risk, so it is reported for sizing rather than as a defect.\n2. **Info:** lib/DEPENDENCIES.md says the vendored sources are unmodified, but nine files differ from upstream by formatting. Semantics are unchanged.\n\n**Coverage:** all 13 listed entry points are marked holds with reasons, plus three invariant rows. Scratch tests were run and removed. Nothing else in the tree was changed.","treeHash":null,"usage":{"cachedInputTokens":1470407,"inputTokens":450,"model":"claude-fable-5-1","outputTokens":25542,"runtime":"claude","turns":36,"wallClockMs":392613}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"f9a12c57a094ca8d","findings":[{"citation":"resolved","description":"Not a permission bypass: this records the privileged power so the judge can weigh it. reportResult and (pre-timeout) cancelRace are gated only on race.reporter, snapshotted at createRace from resultReporter. placeBet does not exclude race.reporter or race.sponsor, so the reporter can hold a position in the race it settles. setResultReporter changes only future races and there is no owner-side cancel, so if a reporter key is compromised after a race opens, the open race can only be settled by that key or refunded by the public timeout at resolveBy. Both powers are documented in README.md as the trust model; no unprivileged amplifier exists (the reporter's result cannot be reported before closesAt, so no bettor can react to it, and all accounting is per-race). Reported as information for the operator and launch owner, not as a defect; a mitigation (owner emergency cancel, or excluding reporter/sponsor from placeBet) would be a design change.","line":198,"path":"src/HamsterRaces.sol","reproduction":"State: owner calls setResultReporter(R); createRace(ref, 3, closesAt, 1000e18). Alice bets 10 ETH on hamster 1; R (from its own key) bets 1 wei on hamster 2 via placeBet (accepted: no reporter exclusion at src/HamsterRaces.sol:178-191). At closesAt, R calls reportResult(id, 2): accepted (winner 2 has stake 1 wei), race Settled, R claims ethRemaining = 10 ETH + 1 wei; Alice receives 1000e18 HAMSTER as the only loser. Expected under an honest reporter: the externally observed winner is reported. Actual: outcome is fully at R's discretion; owner's later setResultReporter(R2) does not change race.reporter and owner cannot cancel (cancelRace reverts CancellationNotAllowed for owner before resolveBy).","severity":"info","snippet":"        if (msg.sender != race.reporter) revert NotReporter();","title":"Trust assumption: the per-race reporter alone decides outcome and voiding, may itself bet, and the owner has no veto over an open race"}],"hash":"7dbbe14e1e91475b2f3a0d987273584afe95a84d3504c9b07c26160b1e6f0ad7","nodeId":"134a76ac-459b-4bb0-9815-1078e3da3d52","outcome":"completed","summary":"The review is complete and `.imd-findings.json` holds the result. The working tree is otherwise unchanged.\n\n**Outcome: no exploitable defect in the Access Control, Trust Gap or Asymmetry areas.** The permission model of `HamsterRaces` is consistent: owner-only configuration and race creation, a per-race snapshotted reporter for results and early voiding, public timeout cancellation, bettor-only claims keyed by sender, and sponsor-only reward reclaim. Paired flows mirror each other (bet and claim update the same per-race ledgers, settle and cancel are one-shot via status, reporter changes and ownership transfers do not redirect in-flight value). The token is unmodified OpenZeppelin ERC-20 with a one-time constructor mint and no admin surface.\n\nWhat I checked and how:\n\n- **Mapped every role and guard** per the Access Control and Trail of Bits inventories. Each of the 13 entry points traced; none has a guard weaker than its effect.\n- **Trust Gap seams**: no admin setter alters pending value, no privileged path chains into a formula an outsider profits from, no sandwichable admin action.\n- **Asymmetry diffs**: winner and loser payout formulas both use original pools with a last-claimant remainder. Both the reporting and the betting windows are disjoint at the second boundary. Allowances from a previous owner cannot be spent by a new owner.\n- **Static-analysis leads**: the slither reentrancy line in `createRace` is owner-only, guarded by `nonReentrant`, and the token has no hooks. The zero-check and timestamp leads are already handled or are intended deadline logic.\n- **Verification**: the project's 49 existing tests pass, and a scratch test of the access edges (reporter snapshot, owner unable to report or cancel, non-sponsor reclaim, stranger claim, timeout timing, ownership transfer) passed before I removed it.\n\nThe one entry in the file is an **info-level trust assumption**, not a defect: the reporter alone decides the result or voids a race, is not excluded from betting, and the owner cannot cancel an already open race if the reporter key is compromised. It carries a concrete scenario so the judge can weigh it. The README documents this model, so I did not rate it as a finding.\n\nCoverage: 13 of 13 entry points answered (12 hold, 1 references the info entry) plus two invariant rows. Nothing was left unreached. I did not run the Economic or Math guides in depth since they belong to other specialists.","treeHash":null,"usage":{"cachedInputTokens":416176,"inputTokens":194,"model":"claude-fable-5-1","outputTokens":18984,"runtime":"claude","turns":21,"wallClockMs":279472}},{"artifacts":[],"attempt":1,"bundleHash":"0679ddeb78037068056093ae6504b2e2e09e0e7dbec85e239b61f2675da6ecb8","device":"589ef002581a5371","findings":[],"hash":"cee47de1c0bf611222bab8632809684712b373b69cf40511075cb727b9390c56","nodeId":"dde76baf-8b49-4dd5-8721-5abb8e0807aa","outcome":"completed","summary":"The revision is complete. Only launch.json changed.\n\n**What I found.** The accepted manifest from the previous round was already correct on every checked field: token contract HamsterToken with no constructor arguments, name and symbol HAMSTER, 18 decimals, totalSupply equal to the constructor's single mint to msg.sender, HamsterRaces taking `$token` then `$requester` in declaration order, and pool and economics copied verbatim. No findings were attached to the brief.\n\n**What I changed.** One sentence in notes was stale. A later accepted commit by another contributor changed HamsterRaces so losers share only the losing side's pro-rata slice of the escrowed reward budget, and the sponsor may reclaim the winning side's unused slice after resolveBy. The notes now describe that behavior instead of the older \"losers share the whole budget\" wording. Everything else in the file is untouched.\n\n**Verification.**\n- `forge build` succeeds.\n- `forge test` with the verifier's fuzz seed: 50 passed, 0 failed across 6 suites.\n- A scratch check confirmed the manifest uses only schema keys, lowercase addresses, string uint256 values, contract names within 32 characters, and notes at 1,535 characters.\n- `git status` shows launch.json as the only modified file.","treeHash":"5c6ab088033e20df510bebfaa80abc70b95ad010","usage":{"cachedInputTokens":282399,"inputTokens":194,"model":"claude-fable-5-1","outputTokens":3370,"runtime":"claude","turns":7,"wallClockMs":76700}},{"artifacts":[],"attempt":1,"bundleHash":"7fa11f689f2e746c7944e79f20470155a2853ac1d11687bd5d960b3cef2584fc","device":"707624586655cf74","findings":[{"description":"The HAMSTER reward budget is split pro rata over all stake on non-winning hamsters, with no link to whether the bettor actually lost ETH overall. Bets are one per address but addresses are free, and the parimutuel ETH pool has no fee. So a participant who backs every hamster from separate wallets always gets back their full ETH (winners split the whole pot) and still collects HAMSTER on the losing legs. If nobody else bets they take the whole budget. If others bet, staking k times the existing pool in the same proportions just before closesAt breaks even on ETH and takes k/(1+k) of the budget, which pushes honest losers' rewards toward zero. The sponsor's budget meant for real losers ends up with sybil hedgers. The fix is a design choice for the author, e.g. a house fee on the ETH pool so hedging has a cost, or rewards based on the net ETH a bettor lost.","line":247,"path":"src/HamsterRaces.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {HamsterToken} from \"src/HamsterToken.sol\";\nimport {HamsterRaces} from \"src/HamsterRaces.sol\";\n\n/// @notice A bettor who backs every hamster from two addresses loses no ETH yet collects the whole\n/// \"lose to earn\" HAMSTER budget. Passes once rewards only accrue to bets that actually lost ETH.\ncontract HedgeFarmProof is Test {\n    HamsterToken token;\n    HamsterRaces races;\n    address owner = makeAddr(\"owner\");\n    address reporter = makeAddr(\"reporter\");\n    address farmerA = makeAddr(\"farmer wallet A\");\n    address farmerB = makeAddr(\"farmer wallet B\");\n    address farmerSink = makeAddr(\"farmer sink\");\n\n    function test_HedgedBettorCollectsRewardsWithoutLosingEth() public {\n        vm.warp(1_000_000);\n        token = new HamsterToken();\n        races = new HamsterRaces(token, owner);\n        token.transfer(owner, 1_000 ether);\n        vm.startPrank(owner);\n        races.setResultReporter(reporter);\n        token.approve(address(races), 1_000 ether);\n        uint256 id = races.createRace(keccak256(\"race\"), 2, uint64(block.timestamp + 1 hours), 1_000 ether);\n        vm.stopPrank();\n\n        vm.deal(farmerA, 1 ether);\n        vm.deal(farmerB, 1 ether);\n        vm.prank(farmerA);\n        races.placeBet{value: 1 ether}(id, 1);\n        vm.prank(farmerB);\n        races.placeBet{value: 1 ether}(id, 2);\n\n        vm.warp(block.timestamp + 1 hours);\n        vm.prank(reporter);\n        races.reportResult(id, 1);\n\n        vm.prank(farmerA);\n        races.claim(id, payable(farmerSink));\n        vm.prank(farmerB);\n        races.claim(id, payable(farmerSink));\n\n        // The farmer put in 2 ETH and got 2 ETH back: no loss was taken.\n        assertEq(farmerSink.balance, 2 ether);\n        // Expected: no \"loser\" reward for a position that lost nothing. Actual: the whole 1,000 HAMSTER budget.\n        assertEq(token.balanceOf(farmerSink), 0, \"hedged bettor farmed the reward budget risk-free\");\n    }\n}","reproduction":"2-hamster race with a 1,000 HAMSTER budget. Wallet A bets 1 ETH on hamster 1, wallet B (same owner) bets 1 ETH on hamster 2, nobody else bets. The reporter reports hamster 1, and both wallets claim to one sink. Expected: the sink gets 2 ETH back and 0 HAMSTER, since no loss was taken. Actual: 2 ETH plus all 1,000 HAMSTER. Run the proof with forge test --match-path on it: it fails with 'hedged bettor farmed the reward budget risk-free: 1000000000000000000000 != 0'.","severity":"medium","title":"Loser rewards can be farmed risk-free by betting on every hamster from several addresses"},{"description":"Outcomes come from an external race that the reporter attests. placeBet accepts entries until block.timestamp < closesAt, which the owner sets independently of when the real race starts. If the real race starts or finishes before closesAt, anyone who sees the result can bet on the known winner and take the winners' pot from honest bettors. The contract does not enforce that betting closes before the race starts. Operators must choose closesAt before the event start, and the code does not document this.","line":180,"path":"src/HamsterRaces.sol","reproduction":"Owner creates a race with closesAt = T + 1h, but the physical race finishes at T + 30m with hamster 2 winning. At T + 31m a bettor calls placeBet{value: 100 ether}(id, 2), which succeeds. After reportResult(id, 2) that bettor takes about 100/(100 + honest stake on 2) of the whole pot. Expected: no entries after the outcome is observable. Actual: entries accepted until closesAt.","severity":"low","title":"Bets stay open until closesAt even if the external race has already been decided"},{"description":"Accounting correctly ignores direct token transfers and forced ETH (e.g. selfdestruct or coinbase), so entitlements are unaffected. But no function sweeps the excess above ethLiability/rewardLiability, so such funds can never be recovered. The existing invariant suite (HamsterInvariant.t.sol, donate) confirms that donations stay in the contract after every claim and reclaim.","line":267,"path":"src/HamsterRaces.sol","reproduction":"token.transfer(address(races), 5 ether), then settle and drain every race. Expected: some recovery path for the excess. Actual: balanceOf(races) == 5 ether permanently, with rewardLiability == 0.","severity":"info","title":"HAMSTER or ETH sent to HamsterRaces outside createRace/placeBet is locked forever"}],"hash":"d5f8bb60c7262805b98920c056fb1e69148668ccdf52bd142ae3901f740c4dc1","nodeId":"5183ade7-40ab-41ae-ac6b-dc1a998dc8f4","outcome":"completed","summary":"I added two test files. `forge build` succeeds, and `forge test` with the verifier's fuzz seed passes all 53 tests in 7 suites. I didn't commit anything.\n\nI also found one real design problem in `HamsterRaces` and reported it in `.imd-findings.json` instead of writing a test that treats it as correct. Two smaller findings are in the same file.\n\n**New tests**\n- **`test/HamsterRacesLedgerInvariant.t.sol`**: random sequences of calls (256 runs × 64 calls) with separate owner, reporter, sponsor and bettor roles. The sequences also change the result reporter, send claims to third-party addresses, and try calls that should be refused. After every step it checks that:\n  - The ETH and HAMSTER the contract holds equal exactly what it still owes, race by race, and the total supply never changes.\n  - Each race's paid-out plus remaining ETH equals what was bet, and the same holds for its reward budget.\n  - A race's terms (reporter, sponsor, deadlines, number of hamsters, budget) never change, even after the reporter or owner changes, and a finished race never reopens.\n  - Every payout matches the bet's outcome: a cancelled race refunds exactly the stake, a winner gets their proportional share of the pot plus at most a few wei of rounding, a loser gets 0 ETH and their proportional share of HAMSTER, and no bet is paid in both.\n  - No call that should be refused gets through: results from a non-reporter, early cancels by an outsider, reclaims by a non-sponsor, reclaims of rewards still owed, claims without a bet, double claims, and bets after closing.\n  - After every run it finishes all races and checks that both ETH and HAMSTER can be fully withdrawn.\n\n  I confirmed this suite actually reaches winner and loser payouts, so its checks are not passing on empty runs.\n- **`test/HamsterRacesPayoutFuzz.t.sol`**: fuzz tests (1,000 runs each) that check:\n  - Payout fairness with 2–8 bettors in any claim order, at stake sizes from 1 wei up to 10³⁰ wei.\n  - That reward budgets of 1, 2 and 7 wei and 500k tokens are split exactly.\n  - The exact time limits for reporting a result and for betting.\n  - Cancellation edge cases, including a reported winner that nobody bet on, which voids the race.\n\n**Findings**\n1. **Medium: the HAMSTER rewards can be collected without risk.** Someone who bets on every hamster from separate wallets always gets all their ETH back and still collects the losers' HAMSTER. With two wallets betting 1 ETH each on a two-hamster race, they get 2 ETH back plus the entire 1,000 HAMSTER budget. When other people bet, a large enough hedge still takes most of the budget away from genuine losers. The report includes a self-contained test that fails on the current code. Fixing it is a design decision for the author, such as a fee on the ETH pool or rewards based on the ETH a bettor actually lost.\n2. **Low:** Betting stays open until the closing time the owner chose, even if the real race has already finished. The contract does nothing to stop people betting on a known winner.\n3. **Info:** Tokens or ETH sent directly to the contract are locked forever, because nothing can withdraw the excess. Nobody's entitlements are affected.","treeHash":"3978672cb375e6d8dedbae729e941941e9a96622","usage":{"cachedInputTokens":1322453,"inputTokens":42,"model":"claude-opus-5-5","outputTokens":29168,"runtime":"claude","turns":22,"wallClockMs":439444}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"6b37e4ab65246705","findings":[{"citation":"resolved","description":"Settling finding e364f931 (info, provenance). The author's fix is correct for OpenZeppelin: the sentence now says forge fmt reformatted utils/Address.sol and token/ERC20/extensions/IERC20Permit.sol, and I re-confirmed those two differ by whitespace only. The same sentence still asserts every other vendored source is unmodified, and that is not true for forge-std: the archive at the recorded URL has the recorded SHA-256 (45157353...ee94), yet seven vendored files (src/console.sol, src/StdToml.sol, src/StdJson.sol, src/StdAssertions.sol, src/Vm.sol, src/interfaces/IERC7540.sol, src/interfaces/IMulticall3.sol) differ from it. audit_flow reported these in the previous round and I merged that into my finding but listed only the OZ files, so the author's fix matches what I wrote rather than the full set. Every difference is whitespace: with all whitespace stripped each of the seven hashes identically to upstream, so nothing executable changed, and forge-std is a test-only dependency that is never deployed. Impact is limited to anyone verifying the vendored closure by file hash. Fix: extend the sentence to say forge fmt also reformatted those seven forge-std files, or restore their upstream bytes. No change to src/ is needed and this does not block the launch.","line":13,"path":"lib/DEPENDENCIES.md","reproduction":"From the repository root: curl -sSL -o fs.tgz https://codeload.github.com/foundry-rs/forge-std/tar.gz/refs/tags/v1.9.7 (sha256sum gives 45157353ab49eab01d294565866731e599b32401757229689ee459aa26b7ee94, matching DEPENDENCIES.md), tar xzf fs.tgz, then for each f in lib/forge-std/src/**/*.sol run cmp lib/forge-std/$f forge-std-1.9.7/$f. Expected (per the sentence at line 13): every file identical except the two OpenZeppelin files it names. Actual: src/console.sol, src/StdToml.sol, src/StdJson.sol, src/StdAssertions.sol, src/Vm.sol, src/interfaces/IERC7540.sol and src/interfaces/IMulticall3.sol differ. tr -d ' \\t\\n\\r' on each pair followed by sha256sum gives identical hashes for all seven, confirming whitespace-only differences. Run this round against the revised tree; all other vendored OZ and forge-std files are byte-identical to upstream.","severity":"info","snippet":"Sources are unmodified except that `forge fmt` reformatted whitespace in `utils/Address.sol` and `token/ERC20/extensions/IERC20Permit.sol` (with all whitespace removed they hash identically to upstream; no code changed).","title":"lib/DEPENDENCIES.md now names the two reformatted OpenZeppelin files but still says every other vendored source is unmodified; seven forge-std files also differ from v1.9.7 by whitespace"}],"hash":"e18fee2cc4e925ecc3d3f7578143e3064b1962b98449700f16e11f17c4d97af9","nodeId":"9cc22519-7bfb-4313-aa3f-39f1c2f51628","outcome":"completed","summary":"The review is complete and the findings file is written. Nothing else is needed.\n\n**Outcome: the medium finding is fixed, the trust-model advisory is dropped, and one info item remains.**\n\n- **Hedge capture (medium, settled as fixed).** Both attached proofs now pass on the revised code. A lone hedger covering three hamsters with 1 wei each gets its 3 wei back, receives two thirds of the budget, and the sponsor reclaims the remaining third at resolveBy. The loser pool is fixed at settlement as the budget times losing stake over total stake, so each losing wei earns at most one rate and no address set can take the whole budget. My own scratch tests confirmed the new paths: pool rounding to zero, dust to the last loser, two concurrent races, no-loser races, timeout cancellation, and zero residual liability in every case. The README states plainly that the hedge is risk-free and can still collect the losing side's share. The full suite passes with 59 tests, including the ledger invariant.\n\n- **Reporter trust model (info, dropped).** The author disputed it as the documented design and added the missing README sentences about the reporter and sponsor betting, no owner veto, and setting closesAt before the physical race. It reproduces but is not a defect, so I did not restate it.\n\n- **Dependency provenance (info, kept).** The amended sentence in lib/DEPENDENCIES.md correctly names the two reformatted OpenZeppelin files, but still claims every other vendored source is unmodified. I downloaded forge-std v1.9.7, whose hash matches the record, and seven of its vendored files differ by whitespace only. That set was in audit_flow's original report and I omitted it from my merged finding last round, so the author fixed exactly what I wrote. Non-blocking, test-only dependency.\n\nAll thirteen entry points are answered as holding, with the static-analysis leads checked and rejected. The scratch directory was removed, and the tree is otherwise unchanged.","treeHash":null,"usage":{"cachedInputTokens":1095211,"inputTokens":546,"model":"claude-fable-5-1","outputTokens":14632,"runtime":"claude","turns":18,"wallClockMs":332004}},{"artifacts":[],"attempt":1,"bundleHash":"b1b1a96796cd99e99c3fead9e4c31fd805674f3835af3aa7382629e58d1f5eed","device":"403561621f0b349a","findings":[],"hash":"f5a54aa78be3b4ebad0c5dd0a9df886dfe414c84680c21047e1ce4194062de76","nodeId":"fa6feb51-a1d9-4f4c-a51f-3b436f4621ad","outcome":"completed","summary":"Implemented HAMSTER with 1 billion tokens, 18 decimals, minted once to the deployer, plus race betting with prefunded loss rewards and timeout refunds.\n\nValidation passed: `forge build`, all 47 local tests with the required fuzz seed, offline checks, and `forge fmt --check`.\n\nDeployment and operations are documented in README.md. Races require an owner-configured trusted result reporter and funded rewards.","treeHash":"07f2130964d8857f8aab65bad0aef39b4e6a2b6b","usage":{"cachedInputTokens":942464,"inputTokens":72050,"model":"gpt-6-astra","outputTokens":33410,"runtime":"codex","turns":6,"wallClockMs":934779}}],"verification":[{"checks":[{"durationMs":2273,"exitCode":0,"name":"build","output":"Compiling 40 files with Solc 0.8.26\nSolc 0.8.26 finished in 2.20s\nCompiler run successful!\n","passed":true},{"durationMs":1980,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 8 tests for test/HamsterRacesAdversarial.t.sol:HamsterRacesAdversarialTest\n[PASS] test_FailedRewardFundingLeavesNoRaceOrLiability() (gas: 311572)\n[PASS] test_FailedSponsorRefundCanBeRetried() (gas: 255124)\n[PASS] test_FailedTokenPayoutRollsBackAndCanBeRetried() (gas: 735855)\n[PASS] test_NativeCurrencyOutsideBetIsRejected() (gas: 41911)\n[PASS] test_ReentrantRefundCannotDrainAnotherRace() (gas: 1265370)\n[PASS] test_ReentrantWinnerCannotClaimTwice() (gas: 1116224)\n[PASS] test_RevertingRecipientDoesNotBlockSettlementOrOtherClaims() (gas: 1397552)\n[PASS] test_ShortRewardFundingRevertsAtomically() (gas: 1014008)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 31.87ms (2.16ms CPU time)\n\nRan 12 tests for test/HamsterRaces.t.sol:HamsterRacesTest\n[PASS] testFuzz_SettlementConservesBothAssets(uint96,uint96,uint96,uint96,bool) (runs: 512, μ: 1196726, ~: 1196768)\nLogs:\n  Bound result 50710853051927800218\n  Bound result 65557490515453477616\n  Bound result 45206\n  Bound result 14264337593543950332\n\n[PASS] test_AnyoneCanCancelAtExactTimeout() (gas: 356667)\n[PASS] test_ClaimsCanBeDirectedByBettorAndDoNotExpire() (gas: 618697)\n[PASS] test_CreationEscrowsRewardsAndPublishesTerms() (gas: 129770)\n[PASS] test_DonationsDoNotChangeEntitlements() (gas: 769878)\n[PASS] test_EmptyRaceCanFinalizeAndReturnBudget() (gas: 124402)\n[PASS] test_NoLosingBetsReturnsStakesAndUnusedRewards() (gas: 620959)\n[PASS] test_NoWinningBetsMeansFullRefunds() (gas: 566996)\n[PASS] test_RacesAndTheirEscrowsAreIsolated() (gas: 1304839)\n[PASS] test_ReporterCanVoidAndAllStakesAreRefunded() (gas: 570715)\n[PASS] test_SmallestRewardUnitsAndDustAreAccountedFor() (gas: 1196261)\n[PASS] test_WinnersReceiveEthAndLosersEarnHamster() (gas: 1216877)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 40.99ms (42.90ms CPU time)\n\nRan 13 tests for test/HamsterToken.t.sol:HamsterTokenTest\n[PASS] testFuzz_DelegatedTransferUsesOnlyAllowance(uint256,uint256) (runs: 512, μ: 155418, ~: 157400)\nLogs:\n  Bound result 10760\n  Bound result 419\n\n[PASS] testFuzz_TransfersConserveSupply(uint256) (runs: 512, μ: 171841, ~: 172366)\nLogs:\n  Bound result 835632405390710433766726567\n\n[PASS] test_AllowanceCanBeReplacedRevokedAndInfinite() (gas: 268892)\n[PASS] test_ConstructorEmitsSingleMint() (gas: 9170)\n[PASS] test_DeploymentRuntimeContainsNoForbiddenOpcode() (gas: 795123)\n[PASS] test_FactoryGetsSupplyAndApplicationsNeverTakeIt() (gas: 722778)\n[PASS] test_InsufficientBalanceRevertsAndPreservesAllowance() (gas: 146244)\n[PASS] test_MetadataAndEntireSupply() (gas: 61165)\n[PASS] test_NoPrivilegedMintFreezeSeizeOrUpgrade() (gas: 680177)\n[PASS] test_RejectsNativeCurrency() (gas: 36175)\n[PASS] test_TransferWithoutApprovalAndOverspendRevert() (gas: 133305)\n[PASS] test_ZeroAddressOperationsRevert() (gas: 171937)\n[PASS] test_ZeroAndSelfTransfers() (gas: 115860)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 40.96ms (57.99ms CPU time)\n\nRan 13 tests for test/HamsterRacesValidation.t.sol:HamsterRacesValidationTest\n[PASS] test_CannotAlterSettledRaceOrCancelEarlyWithoutReporter() (gas: 397150)\n[PASS] test_ClaimsRejectEarlyMissingDuplicateAndInvalidRecipient() (gas: 483011)\n[PASS] test_ConstructorNeedsNoExternalCodeButFirstUseChecksToken() (gas: 105993)\n[PASS] test_CreationRejectsUnauthorizedInvalidDuplicateAndUnfundedRaces() (gas: 583281)\n[PASS] test_DeploymentRequiresExplicitNonzeroOwnerAndToken() (gas: 6042)\n[PASS] test_EntryClosesExactlyAtDeadlineAndResultCanThenSettle() (gas: 303563)\n[PASS] test_MaximumEntryWindowAndHamsterCountAccepted() (gas: 330758)\n[PASS] test_RejectsUnknownRaceEverywhere() (gas: 174049)\n[PASS] test_RejectsZeroInvalidAndDuplicateBets() (gas: 387128)\n[PASS] test_ReporterMustBeConfiguredByOwnerBeforeCreation() (gas: 528002)\n[PASS] test_ReporterRotationAndOwnershipChangesNeverRewriteExistingTerms() (gas: 733704)\n[PASS] test_ResultRejectsUnauthorizedEarlyInvalidLateAndDuplicateCalls() (gas: 571926)\n[PASS] test_SponsorCannotTakeActiveOrOwedRewardsAndCannotReclaimTwice() (gas: 1021071)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 44.18ms (3.51ms CPU time)\n\nRan 3 tests for test/HamsterRacesHedge.t.sol:HamsterRacesHedgeTest\n[PASS] testFuzz_LossRewardNeverExceedsBudgetPerTotalStake(uint96,uint96,uint96,uint8) (runs: 512, μ: 1278182, ~: 1269164)\nLogs:\n  Bound result 3091413249943063300\n  Bound result 572446492249928\n  Bound result 1\n  Bound result 1\n\n[PASS] test_SoloHedgeCannotTakeTheWholeBudgetAndSponsorRecoversTheRest() (gas: 1113425)\n[PASS] test_SponsorReclaimNeverTouchesUnclaimedLossRewards() (gas: 952468)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 47.20ms (47.19ms CPU time)\n\nRan 1 test for test/HamsterInvariant.t.sol:HamsterInvariantTest\n[PASS] invariant_EscrowsConserveBothAssetsAndSupply() (runs: 128, calls: 8192, reverts: 0)\n\n╭-------------+-------------+-------+---------+----------╮\n| Contract    | Selector    | Calls | Reverts | Discards |\n+========================================================+\n| RaceHandler | advanceTime | 1099  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | bet         | 998   | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | cancel      | 1009  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | claim       | 1013  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | donate      | 1019  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | openRace    | 1051  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | reclaim     | 1022  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | settle      | 981   | 0       | 0        |\n╰-------------+-------------+-------+---------+----------╯\n\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 3\n  Bound result 3600\n  Bound result 9296\n  Bound result 299637807644260\n  Bound result 2\n  Bound result 3\n  Bound result 1\n  Bound result 25\n  Bound result 2\n  Bound result 3\n  Bound result 255\n  Bound result 4304\n  Bound result 7\n  Bound result 9541\n  Bound result 1995281824\n  Bound result 8\n  Bound result 2020\n  Bound result 1167\n  Bound result 4\n  Bound result 6400\n  Bound result 2703\n  Bound result 43200\n  Bound result 23292\n  Bound result 127\n  Bound result 5681\n  Bound result 4\n  Bound result 9436\n  Bound result 6815651440749796317\n  Bound result 6400\n  Bound result 79829455070171900516\n  Bound result 1\n  Bound result 4\n  Bound result 10018967\n  Bound result 6\n  Bound result 1\n  Bound result 557\n  Bound result 10\n  Bound result 1\n  Bound result 9\n  Bound result 8996\n  Bound result 2763728217043597703217\n  Bound result 3\n  Bound result 1220\n  Bound result 8\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.89s (1.89s CPU time)\n\nRan 6 test suites in 1.90s (2.10s CPU time): 50 tests passed, 0 failed, 0 skipped (50 total tests)\n","passed":true},{"durationMs":44,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"HamsterRaces.acceptOwnership()\",\"HamsterRaces.cancelRace(uint256)\",\"HamsterRaces.claim(uint256,address)\",\"HamsterRaces.createRace(bytes32,uint8,uint64,uint256)\",\"HamsterRaces.placeBet(uint256,uint8)\",\"HamsterRaces.reclaimRewards(uint256,address)\",\"HamsterRaces.renounceOwnership()\",\"HamsterRaces.reportResult(uint256,uint8)\",\"HamsterRaces.setResultReporter(address)\",\"HamsterRaces.transferOwnership(address)\",\"HamsterToken.approve(address,uint256)\",\"HamsterToken.transfer(address,uint256)\",\"HamsterToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":3,\"README.md\":241,\"SECURITY.md\":72,\"foundry.toml\":21,\"remappings.txt\":2,\"src/HamsterRaces.sol\":319,\"src/HamsterToken.sol\":14,\"test/HamsterInvariant.t.sol\":207,\"test/HamsterRaces.t.sol\":253,\"test/HamsterRacesAdversarial.t.sol\":197,\"test/HamsterRacesHedge.t.sol\":100,\"test/HamsterRacesValidation.t.sol\":259,\"test/HamsterToken.t.sol\":222,\"test/helpers/RaceTestBase.sol\":64},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":1045,"exitCode":0,"name":"slither","output":"[high/medium] reentrancy-balance at src/HamsterRaces.sol:138: Reentrancy in HamsterRaces.createRace(bytes32,uint8,uint64,uint256) (src/HamsterRaces.sol#138-177):\n[low/medium] missing-zero-check at src/HamsterRaces.sol:235: HamsterRaces.claim(uint256,address).recipient (src/HamsterRaces.sol#235) lacks a zero-check on :\n[low/medium] timestamp at src/HamsterRaces.sol:198: HamsterRaces.reportResult(uint256,uint8) (src/HamsterRaces.sol#198-221) uses timestamp for comparisons\n[low/medium] timestamp at src/HamsterRaces.sol:180: HamsterRaces.placeBet(uint256,uint8) (src/HamsterRaces.sol#180-193) uses timestamp for comparisons\n[low/medium] timestamp at src/HamsterRaces.sol:277: HamsterRaces.reclaimRewards(uint256,address) (src/HamsterRaces.sol#277-290) uses timestamp for comparisons\n[low/medium] timestamp at src/HamsterRaces.sol:138: HamsterRaces.createRace(bytes32,uint8,uint64,uint256) (src/HamsterRaces.sol#138-177) uses timestamp for comparisons\n[low/medium] timestamp at src/HamsterRaces.sol:225: HamsterRaces.cancelRace(uint256) (src/HamsterRaces.sol#225-230) uses timestamp for comparisons","passed":true},{"durationMs":338,"exitCode":0,"name":"aderyn","output":"[low] centralization-risk at src/HamsterRaces.sol:14: Centralization Risk (3 places)\n[low] large-numeric-literal at src/HamsterToken.sol:9: Large Numeric Literal\n[low] local-variable-shadowing at src/HamsterRaces.sol:56: Local Variable Shadows State Variable","passed":true},{"durationMs":1246,"exitCode":0,"name":"proof cbe8fae35400","output":"Compiling 35 files with Solc 0.8.26\nSolc 0.8.26 finished in 745.29ms\nCompiler run successful!\n\nRan 1 test for test/imd-proof-ba36788a/Proof_cbe8fae35400.t.sol:HedgeCaptureTest\n[PASS] test_riskFreeHedgeTakesTheWholeRewardBudget() (gas: 900051)\nLogs:\n  eth staked by hedger: 3\n  eth returned to hedger: 3\n  hamster paid to hedger: 666666666666666666666\n  hamster left for sponsor: 333333333333333333334\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 972.09µs (348.59µs CPU time)\n\nRan 1 test suite in 3.11ms (972.09µs 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":"031c40f32b6cf1c8ffbadc9e7bbdd8d4b8ad2dc891a88b1cd1d30951834339e3","verifiedTreeHash":"41b8a0939fd5c6a30406ed122dd44cec5793a371","verifierVersion":"0.1.0+a6ee6548"},{"checks":[{"durationMs":2030,"exitCode":0,"name":"build","output":"Compiling 39 files with Solc 0.8.26\nSolc 0.8.26 finished in 1.97s\nCompiler run successful!\n","passed":true},{"durationMs":1862,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 8 tests for test/HamsterRacesAdversarial.t.sol:HamsterRacesAdversarialTest\n[PASS] test_FailedRewardFundingLeavesNoRaceOrLiability() (gas: 311584)\n[PASS] test_FailedSponsorRefundCanBeRetried() (gas: 246405)\n[PASS] test_FailedTokenPayoutRollsBackAndCanBeRetried() (gas: 667971)\n[PASS] test_NativeCurrencyOutsideBetIsRejected() (gas: 41911)\n[PASS] test_ReentrantRefundCannotDrainAnotherRace() (gas: 1260979)\n[PASS] test_ReentrantWinnerCannotClaimTwice() (gas: 1058023)\n[PASS] test_RevertingRecipientDoesNotBlockSettlementOrOtherClaims() (gas: 1334950)\n[PASS] test_ShortRewardFundingRevertsAtomically() (gas: 1014008)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 9.14ms (3.07ms CPU time)\n\nRan 13 tests for test/HamsterToken.t.sol:HamsterTokenTest\n[PASS] testFuzz_DelegatedTransferUsesOnlyAllowance(uint256,uint256) (runs: 512, μ: 155149, ~: 157400)\nLogs:\n  Bound result 848024485669392562137649490\n  Bound result 51730366036754521876613728\n\n[PASS] testFuzz_TransfersConserveSupply(uint256) (runs: 512, μ: 171705, ~: 172366)\nLogs:\n  Bound result 4327876761341\n\n[PASS] test_AllowanceCanBeReplacedRevokedAndInfinite() (gas: 268892)\n[PASS] test_ConstructorEmitsSingleMint() (gas: 9170)\n[PASS] test_DeploymentRuntimeContainsNoForbiddenOpcode() (gas: 795123)\n[PASS] test_FactoryGetsSupplyAndApplicationsNeverTakeIt() (gas: 722778)\n[PASS] test_InsufficientBalanceRevertsAndPreservesAllowance() (gas: 146244)\n[PASS] test_MetadataAndEntireSupply() (gas: 61165)\n[PASS] test_NoPrivilegedMintFreezeSeizeOrUpgrade() (gas: 680177)\n[PASS] test_RejectsNativeCurrency() (gas: 36175)\n[PASS] test_TransferWithoutApprovalAndOverspendRevert() (gas: 133305)\n[PASS] test_ZeroAddressOperationsRevert() (gas: 171937)\n[PASS] test_ZeroAndSelfTransfers() (gas: 115860)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 31.94ms (25.55ms CPU time)\n\nRan 13 tests for test/HamsterRacesValidation.t.sol:HamsterRacesValidationTest\n[PASS] test_CannotAlterSettledRaceOrCancelEarlyWithoutReporter() (gas: 390309)\n[PASS] test_ClaimsRejectEarlyMissingDuplicateAndInvalidRecipient() (gas: 476170)\n[PASS] test_ConstructorNeedsNoExternalCodeButFirstUseChecksToken() (gas: 105993)\n[PASS] test_CreationRejectsUnauthorizedInvalidDuplicateAndUnfundedRaces() (gas: 583281)\n[PASS] test_DeploymentRequiresExplicitNonzeroOwnerAndToken() (gas: 6042)\n[PASS] test_EntryClosesExactlyAtDeadlineAndResultCanThenSettle() (gas: 296722)\n[PASS] test_MaximumEntryWindowAndHamsterCountAccepted() (gas: 326354)\n[PASS] test_RejectsUnknownRaceEverywhere() (gas: 173950)\n[PASS] test_RejectsZeroInvalidAndDuplicateBets() (gas: 382724)\n[PASS] test_ReporterMustBeConfiguredByOwnerBeforeCreation() (gas: 523594)\n[PASS] test_ReporterRotationAndOwnershipChangesNeverRewriteExistingTerms() (gas: 718025)\n[PASS] test_ResultRejectsUnauthorizedEarlyInvalidLateAndDuplicateCalls() (gas: 563118)\n[PASS] test_SponsorCannotTakeActiveOrOwedRewardsAndCannotReclaimTwice() (gas: 972046)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 31.95ms (3.72ms CPU time)\n\nRan 12 tests for test/HamsterRaces.t.sol:HamsterRacesTest\n[PASS] testFuzz_SettlementConservesBothAssets(uint96,uint96,uint96,uint96,bool) (runs: 512, μ: 1064827, ~: 1064837)\nLogs:\n  Bound result 11251\n  Bound result 5801\n  Bound result 67100327374582311123\n  Bound result 1162\n\n[PASS] test_AnyoneCanCancelAtExactTimeout() (gas: 352263)\n[PASS] test_ClaimsCanBeDirectedByBettorAndDoNotExpire() (gas: 560452)\n[PASS] test_CreationEscrowsRewardsAndPublishesTerms() (gas: 125366)\n[PASS] test_DonationsDoNotChangeEntitlements() (gas: 640161)\n[PASS] test_EmptyRaceCanFinalizeAndReturnBudget() (gas: 122486)\n[PASS] test_NoLosingBetsReturnsStakesAndUnusedRewards() (gas: 614097)\n[PASS] test_NoWinningBetsMeansFullRefunds() (gas: 560677)\n[PASS] test_RacesAndTheirEscrowsAreIsolated() (gas: 1178099)\n[PASS] test_ReporterCanVoidAndAllStakesAreRefunded() (gas: 568799)\n[PASS] test_SmallestRewardUnitsAndDustAreAccountedFor() (gas: 1016220)\n[PASS] test_WinnersReceiveEthAndLosersEarnHamster() (gas: 1054302)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 31.98ms (35.37ms CPU time)\n\nRan 1 test for test/HamsterInvariant.t.sol:HamsterInvariantTest\n[PASS] invariant_EscrowsConserveBothAssetsAndSupply() (runs: 128, calls: 8192, reverts: 0)\n\n╭-------------+-------------+-------+---------+----------╮\n| Contract    | Selector    | Calls | Reverts | Discards |\n+========================================================+\n| RaceHandler | advanceTime | 1099  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | bet         | 998   | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | cancel      | 1009  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | claim       | 1013  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | donate      | 1019  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | openRace    | 1051  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | reclaim     | 1022  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | settle      | 981   | 0       | 0        |\n╰-------------+-------------+-------+---------+----------╯\n\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 3\n  Bound result 3600\n  Bound result 9051\n  Bound result 299637807644260\n  Bound result 2\n  Bound result 3\n  Bound result 1\n  Bound result 25\n  Bound result 2\n  Bound result 3\n  Bound result 255\n  Bound result 4321\n  Bound result 7\n  Bound result 9542\n  Bound result 6591\n  Bound result 8\n  Bound result 19080\n  Bound result 6400\n  Bound result 2033\n  Bound result 7428\n  Bound result 1\n  Bound result 43200\n  Bound result 23292\n  Bound result 127\n  Bound result 2004\n  Bound result 9488\n  Bound result 6815651440749796317\n  Bound result 6400\n  Bound result 99999700362192332782\n  Bound result 1\n  Bound result 4\n  Bound result 10018967\n  Bound result 6\n  Bound result 1\n  Bound result 9269\n  Bound result 10\n  Bound result 1\n  Bound result 9\n  Bound result 9007\n  Bound result 8981159846850304451024\n  Bound result 3\n  Bound result 257\n  Bound result 8\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.79s (1.78s CPU time)\n\nRan 5 test suites in 1.79s (1.89s CPU time): 47 tests passed, 0 failed, 0 skipped (47 total tests)\n","passed":true},{"durationMs":38,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"HamsterRaces.acceptOwnership()\",\"HamsterRaces.cancelRace(uint256)\",\"HamsterRaces.claim(uint256,address)\",\"HamsterRaces.createRace(bytes32,uint8,uint64,uint256)\",\"HamsterRaces.placeBet(uint256,uint8)\",\"HamsterRaces.reclaimRewards(uint256,address)\",\"HamsterRaces.renounceOwnership()\",\"HamsterRaces.reportResult(uint256,uint8)\",\"HamsterRaces.setResultReporter(address)\",\"HamsterRaces.transferOwnership(address)\",\"HamsterToken.approve(address,uint256)\",\"HamsterToken.transfer(address,uint256)\",\"HamsterToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":3,\"README.md\":217,\"SECURITY.md\":69,\"foundry.toml\":21,\"launch.json\":32,\"remappings.txt\":2,\"src/HamsterRaces.sol\":309,\"src/HamsterToken.sol\":14,\"test/HamsterInvariant.t.sol\":201,\"test/HamsterRaces.t.sol\":230,\"test/HamsterRacesAdversarial.t.sol\":197,\"test/HamsterRacesValidation.t.sol\":259,\"test/HamsterToken.t.sol\":222,\"test/helpers/RaceTestBase.sol\":64},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"57e3e16f7e6cf1c9cce2f17d0ae7bfe3d34fe6c50dae1d67c158e03374387695","verifiedTreeHash":"c3c4ee9a364a238cb836b317595b4e9b105c715b","verifierVersion":"0.1.0+a6ee6548"},{"checks":[{"durationMs":2966,"exitCode":0,"name":"build","output":"Compiling 42 files with Solc 0.8.26\nSolc 0.8.26 finished in 2.89s\nCompiler run successful!\n","passed":true},{"durationMs":19359,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 3 tests for test/HamsterRacesHedge.t.sol:HamsterRacesHedgeTest\n[PASS] testFuzz_LossRewardNeverExceedsBudgetPerTotalStake(uint96,uint96,uint96,uint8) (runs: 512, μ: 1277913, ~: 1269164)\nLogs:\n  Bound result 1092217953967737897\n  Bound result 49614553593741713\n  Bound result 1\n  Bound result 1\n\n[PASS] test_SoloHedgeCannotTakeTheWholeBudgetAndSponsorRecoversTheRest() (gas: 1113425)\n[PASS] test_SponsorReclaimNeverTouchesUnclaimedLossRewards() (gas: 952468)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 138.86ms (138.98ms CPU time)\n\nRan 8 tests for test/HamsterRacesAdversarial.t.sol:HamsterRacesAdversarialTest\n[PASS] test_FailedRewardFundingLeavesNoRaceOrLiability() (gas: 311572)\n[PASS] test_FailedSponsorRefundCanBeRetried() (gas: 255124)\n[PASS] test_FailedTokenPayoutRollsBackAndCanBeRetried() (gas: 735855)\n[PASS] test_NativeCurrencyOutsideBetIsRejected() (gas: 41911)\n[PASS] test_ReentrantRefundCannotDrainAnotherRace() (gas: 1265370)\n[PASS] test_ReentrantWinnerCannotClaimTwice() (gas: 1116224)\n[PASS] test_RevertingRecipientDoesNotBlockSettlementOrOtherClaims() (gas: 1397552)\n[PASS] test_ShortRewardFundingRevertsAtomically() (gas: 1014008)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 140.67ms (2.84ms CPU time)\n\nRan 13 tests for test/HamsterRacesValidation.t.sol:HamsterRacesValidationTest\n[PASS] test_CannotAlterSettledRaceOrCancelEarlyWithoutReporter() (gas: 397150)\n[PASS] test_ClaimsRejectEarlyMissingDuplicateAndInvalidRecipient() (gas: 483011)\n[PASS] test_ConstructorNeedsNoExternalCodeButFirstUseChecksToken() (gas: 105993)\n[PASS] test_CreationRejectsUnauthorizedInvalidDuplicateAndUnfundedRaces() (gas: 583281)\n[PASS] test_DeploymentRequiresExplicitNonzeroOwnerAndToken() (gas: 6042)\n[PASS] test_EntryClosesExactlyAtDeadlineAndResultCanThenSettle() (gas: 303563)\n[PASS] test_MaximumEntryWindowAndHamsterCountAccepted() (gas: 330758)\n[PASS] test_RejectsUnknownRaceEverywhere() (gas: 174049)\n[PASS] test_RejectsZeroInvalidAndDuplicateBets() (gas: 387128)\n[PASS] test_ReporterMustBeConfiguredByOwnerBeforeCreation() (gas: 528002)\n[PASS] test_ReporterRotationAndOwnershipChangesNeverRewriteExistingTerms() (gas: 733704)\n[PASS] test_ResultRejectsUnauthorizedEarlyInvalidLateAndDuplicateCalls() (gas: 571926)\n[PASS] test_SponsorCannotTakeActiveOrOwedRewardsAndCannotReclaimTwice() (gas: 1021071)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 142.78ms (3.13ms CPU time)\n\nRan 13 tests for test/HamsterToken.t.sol:HamsterTokenTest\n[PASS] testFuzz_DelegatedTransferUsesOnlyAllowance(uint256,uint256) (runs: 512, μ: 155622, ~: 157400)\nLogs:\n  Bound result 8257\n  Bound result 20\n\n[PASS] testFuzz_TransfersConserveSupply(uint256) (runs: 512, μ: 172165, ~: 172366)\nLogs:\n  Bound result 864367661373267408287503769\n\n[PASS] test_AllowanceCanBeReplacedRevokedAndInfinite() (gas: 268892)\n[PASS] test_ConstructorEmitsSingleMint() (gas: 9170)\n[PASS] test_DeploymentRuntimeContainsNoForbiddenOpcode() (gas: 795123)\n[PASS] test_FactoryGetsSupplyAndApplicationsNeverTakeIt() (gas: 722778)\n[PASS] test_InsufficientBalanceRevertsAndPreservesAllowance() (gas: 146244)\n[PASS] test_MetadataAndEntireSupply() (gas: 61165)\n[PASS] test_NoPrivilegedMintFreezeSeizeOrUpgrade() (gas: 680177)\n[PASS] test_RejectsNativeCurrency() (gas: 36175)\n[PASS] test_TransferWithoutApprovalAndOverspendRevert() (gas: 133305)\n[PASS] test_ZeroAddressOperationsRevert() (gas: 171937)\n[PASS] test_ZeroAndSelfTransfers() (gas: 115860)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 153.54ms (305.50ms CPU time)\n\nRan 12 tests for test/HamsterRaces.t.sol:HamsterRacesTest\n[PASS] testFuzz_SettlementConservesBothAssets(uint96,uint96,uint96,uint96,bool) (runs: 512, μ: 1196726, ~: 1196768)\nLogs:\n  Bound result 273496690488480\n  Bound result 1068\n  Bound result 30512\n  Bound result 3\n\n[PASS] test_AnyoneCanCancelAtExactTimeout() (gas: 356667)\n[PASS] test_ClaimsCanBeDirectedByBettorAndDoNotExpire() (gas: 618697)\n[PASS] test_CreationEscrowsRewardsAndPublishesTerms() (gas: 129770)\n[PASS] test_DonationsDoNotChangeEntitlements() (gas: 769878)\n[PASS] test_EmptyRaceCanFinalizeAndReturnBudget() (gas: 124402)\n[PASS] test_NoLosingBetsReturnsStakesAndUnusedRewards() (gas: 620959)\n[PASS] test_NoWinningBetsMeansFullRefunds() (gas: 566996)\n[PASS] test_RacesAndTheirEscrowsAreIsolated() (gas: 1304839)\n[PASS] test_ReporterCanVoidAndAllStakesAreRefunded() (gas: 570715)\n[PASS] test_SmallestRewardUnitsAndDustAreAccountedFor() (gas: 1196261)\n[PASS] test_WinnersReceiveEthAndLosersEarnHamster() (gas: 1216877)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 154.78ms (157.15ms CPU time)\n\nRan 6 tests for test/HamsterRacesPayoutFuzz.t.sol:HamsterRacesPayoutFuzzTest\n[PASS] testFuzz_EveryClaimOrderPaysProportionalSharesAndEmptiesTheRace(uint8,uint256,uint256,uint256,uint256) (runs: 1000, μ: 1480835, ~: 1346820)\nLogs:\n  Bound result 8\n  Bound result 509620972309517238\n  Bound result 303019107972070305\n  Bound result 326222425760525420\n  Bound result 75048798503718249\n  Bound result 928889805653139180\n  Bound result 310948007120946784\n  Bound result 265579542899429796\n  Bound result 914347488137866338\n\n[PASS] testFuzz_ResultWindowBoundaries(uint256) (runs: 1000, μ: 533662, ~: 505050)\nLogs:\n  Bound result 1051299\n\n[PASS] testFuzz_RewardBudgetEdgesSplitExactly(uint256,uint256,uint256) (runs: 1000, μ: 1921365, ~: 1907619)\nLogs:\n  Bound result 4386153335956981565\n  Bound result 26332946089015619\n  Bound result 5732760115909270185\n  Bound result 4644444379077458323\n  Bound result 7306961163930539277\n  Bound result 3604562365867026370\n\n[PASS] test_ReporterCannotCancelAfterSettlingAndOutsiderCannotCancelAtResolveByMinusOne() (gas: 394267)\n[PASS] test_ReportingAWinnerNobodyBackedVoidsTheRaceAndReturnsBudget() (gas: 598366)\n[PASS] test_TinyBudgetRoundsLossPoolToZeroAndIsReclaimableAtOnce() (gas: 870577)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 154.86ms (461.39ms CPU time)\n\nRan 1 test for test/HamsterInvariant.t.sol:HamsterInvariantTest\n[PASS] invariant_EscrowsConserveBothAssetsAndSupply() (runs: 128, calls: 8192, reverts: 0)\n\n╭-------------+-------------+-------+---------+----------╮\n| Contract    | Selector    | Calls | Reverts | Discards |\n+========================================================+\n| RaceHandler | advanceTime | 1099  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | bet         | 998   | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | cancel      | 1009  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | claim       | 1013  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | donate      | 1019  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | openRace    | 1051  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | reclaim     | 1022  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | settle      | 981   | 0       | 0        |\n╰-------------+-------------+-------+---------+----------╯\n\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 3\n  Bound result 3600\n  Bound result 9296\n  Bound result 299637807644260\n  Bound result 2\n  Bound result 3\n  Bound result 1\n  Bound result 25\n  Bound result 2\n  Bound result 3\n  Bound result 255\n  Bound result 4306\n  Bound result 7\n  Bound result 9540\n  Bound result 1995281824\n  Bound result 8\n  Bound result 2020\n  Bound result 1282\n  Bound result 7\n  Bound result 18\n  Bound result 7875\n  Bound result 43200\n  Bound result 23292\n  Bound result 96\n  Bound result 2185\n  Bound result 9542\n  Bound result 6\n  Bound result 6815651440749796317\n  Bound result 5\n  Bound result 91933624045966193265\n  Bound result 3600\n  Bound result 2\n  Bound result 10018967\n  Bound result 6\n  Bound result 1\n  Bound result 553\n  Bound result 10\n  Bound result 6400\n  Bound result 17\n  Bound result 9095\n  Bound result 4934623760108546967306\n  Bound result 15\n  Bound result 1139\n  Bound result 6400\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.91s (1.90s CPU time)\n\nRan 1 test for test/HamsterRacesLedgerInvariant.t.sol:HamsterRacesLedgerInvariantTest\n[PASS]\nHamsterRacesLedgerInvariantTest invariants:\n[PASS] invariant_ContractHoldsExactlyWhatItOwes\n[PASS] invariant_EachRaceConservesStakesAndBudget\n[PASS] invariant_EveryPayoutMatchesItsBetsOutcome\n[PASS] invariant_NoUnauthorizedOrRepeatedActionSucceeds\n[PASS] invariant_TermsAreImmutableAndFinalStatesNeverReopen\n HamsterRacesLedgerInvariantTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭---------------+----------------+-------+---------+----------╮\n| Contract      | Selector       | Calls | Reverts | Discards |\n+=============================================================+\n| LedgerHandler | bet            | 1817  | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| LedgerHandler | cancel         | 1799  | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| LedgerHandler | claim          | 1797  | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| LedgerHandler | openRace       | 1900  | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| LedgerHandler | probe          | 1806  | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| LedgerHandler | reclaim        | 1870  | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| LedgerHandler | report         | 1749  | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| LedgerHandler | rotateReporter | 1774  | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| LedgerHandler | warp           | 1872  | 0       | 0        |\n╰---------------+----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 5000000000000000000000\n  Bound result 4\n  Bound result 10800\n  Bound result 4\n  Bound result 19\n  Bound result 2\n  Bound result 162834\n  Bound result 2\n  Bound result 23264\n  Bound result 2\n  Bound result 1\n  Bound result 2356\n  Bound result 9\n  Bound result 45056\n  Bound result 8323\n  Bound result 4253804986692590097\n  Bound result 1\n  Bound result 8478\n  Bound result 17754\n  Bound result 11\n  Bound result 2\n  Bound result 6437437426837995940214\n  Bound result 9\n  Bound result 86400\n  Bound result 4\n  Bound result 7168\n  Bound result 11957\n  Bound result 9\n  Bound result 13319\n  Bound result 10\n  Bound result 5\n  Bound result 6\n  Bound result 25\n  Bound result 6\n  Bound result 7382\n  Bound result 1\n  Bound result 21600\n  Bound result 56000\n  Bound result 12\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 19.27s (19.27s CPU time)\n\nRan 8 test suites in 19.27s (22.06s CPU time): 57 tests passed, 0 failed, 0 skipped (57 total tests)\n","passed":true},{"durationMs":44,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"HamsterRaces.acceptOwnership()\",\"HamsterRaces.cancelRace(uint256)\",\"HamsterRaces.claim(uint256,address)\",\"HamsterRaces.createRace(bytes32,uint8,uint64,uint256)\",\"HamsterRaces.placeBet(uint256,uint8)\",\"HamsterRaces.reclaimRewards(uint256,address)\",\"HamsterRaces.renounceOwnership()\",\"HamsterRaces.reportResult(uint256,uint8)\",\"HamsterRaces.setResultReporter(address)\",\"HamsterRaces.transferOwnership(address)\",\"HamsterToken.approve(address,uint256)\",\"HamsterToken.transfer(address,uint256)\",\"HamsterToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":3,\"README.md\":241,\"SECURITY.md\":72,\"foundry.toml\":21,\"remappings.txt\":2,\"src/HamsterRaces.sol\":319,\"src/HamsterToken.sol\":14,\"test/HamsterInvariant.t.sol\":207,\"test/HamsterRaces.t.sol\":253,\"test/HamsterRacesAdversarial.t.sol\":197,\"test/HamsterRacesHedge.t.sol\":100,\"test/HamsterRacesLedgerInvariant.t.sol\":463,\"test/HamsterRacesPayoutFuzz.t.sol\":286,\"test/HamsterRacesValidation.t.sol\":259,\"test/HamsterToken.t.sol\":222,\"test/helpers/RaceTestBase.sol\":64},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"71a07757f9e3812e709722585dc722f1ec8f2c1ed3f3c93adb4ef4b2b7c238a8","verifiedTreeHash":"d59cb5812d8e5f935d0ceac8692595905a999e85","verifierVersion":"0.1.0+a6ee6548"},{"checks":[{"durationMs":2218,"exitCode":0,"name":"build","output":"Compiling 40 files with Solc 0.8.26\nSolc 0.8.26 finished in 2.15s\nCompiler run successful!\n","passed":true},{"durationMs":2035,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 13 tests for test/HamsterRacesValidation.t.sol:HamsterRacesValidationTest\n[PASS] test_CannotAlterSettledRaceOrCancelEarlyWithoutReporter() (gas: 397150)\n[PASS] test_ClaimsRejectEarlyMissingDuplicateAndInvalidRecipient() (gas: 483011)\n[PASS] test_ConstructorNeedsNoExternalCodeButFirstUseChecksToken() (gas: 105993)\n[PASS] test_CreationRejectsUnauthorizedInvalidDuplicateAndUnfundedRaces() (gas: 583281)\n[PASS] test_DeploymentRequiresExplicitNonzeroOwnerAndToken() (gas: 6042)\n[PASS] test_EntryClosesExactlyAtDeadlineAndResultCanThenSettle() (gas: 303563)\n[PASS] test_MaximumEntryWindowAndHamsterCountAccepted() (gas: 330758)\n[PASS] test_RejectsUnknownRaceEverywhere() (gas: 174049)\n[PASS] test_RejectsZeroInvalidAndDuplicateBets() (gas: 387128)\n[PASS] test_ReporterMustBeConfiguredByOwnerBeforeCreation() (gas: 528002)\n[PASS] test_ReporterRotationAndOwnershipChangesNeverRewriteExistingTerms() (gas: 733704)\n[PASS] test_ResultRejectsUnauthorizedEarlyInvalidLateAndDuplicateCalls() (gas: 571926)\n[PASS] test_SponsorCannotTakeActiveOrOwedRewardsAndCannotReclaimTwice() (gas: 1021071)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 33.34ms (3.29ms CPU time)\n\nRan 8 tests for test/HamsterRacesAdversarial.t.sol:HamsterRacesAdversarialTest\n[PASS] test_FailedRewardFundingLeavesNoRaceOrLiability() (gas: 311572)\n[PASS] test_FailedSponsorRefundCanBeRetried() (gas: 255124)\n[PASS] test_FailedTokenPayoutRollsBackAndCanBeRetried() (gas: 735855)\n[PASS] test_NativeCurrencyOutsideBetIsRejected() (gas: 41911)\n[PASS] test_ReentrantRefundCannotDrainAnotherRace() (gas: 1265370)\n[PASS] test_ReentrantWinnerCannotClaimTwice() (gas: 1116224)\n[PASS] test_RevertingRecipientDoesNotBlockSettlementOrOtherClaims() (gas: 1397552)\n[PASS] test_ShortRewardFundingRevertsAtomically() (gas: 1014008)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 39.00ms (2.51ms CPU time)\n\nRan 3 tests for test/HamsterRacesHedge.t.sol:HamsterRacesHedgeTest\n[PASS] testFuzz_LossRewardNeverExceedsBudgetPerTotalStake(uint96,uint96,uint96,uint8) (runs: 512, μ: 1278182, ~: 1269164)\nLogs:\n  Bound result 10871\n  Bound result 2370\n  Bound result 5594\n  Bound result 2\n\n[PASS] test_SoloHedgeCannotTakeTheWholeBudgetAndSponsorRecoversTheRest() (gas: 1113425)\n[PASS] test_SponsorReclaimNeverTouchesUnclaimedLossRewards() (gas: 952468)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 47.30ms (47.38ms CPU time)\n\nRan 12 tests for test/HamsterRaces.t.sol:HamsterRacesTest\n[PASS] testFuzz_SettlementConservesBothAssets(uint96,uint96,uint96,uint96,bool) (runs: 512, μ: 1196726, ~: 1196768)\nLogs:\n  Bound result 3208337807529062099\n  Bound result 3\n  Bound result 57666106609622052305\n  Bound result 21\n\n[PASS] test_AnyoneCanCancelAtExactTimeout() (gas: 356667)\n[PASS] test_ClaimsCanBeDirectedByBettorAndDoNotExpire() (gas: 618697)\n[PASS] test_CreationEscrowsRewardsAndPublishesTerms() (gas: 129770)\n[PASS] test_DonationsDoNotChangeEntitlements() (gas: 769878)\n[PASS] test_EmptyRaceCanFinalizeAndReturnBudget() (gas: 124402)\n[PASS] test_NoLosingBetsReturnsStakesAndUnusedRewards() (gas: 620959)\n[PASS] test_NoWinningBetsMeansFullRefunds() (gas: 566996)\n[PASS] test_RacesAndTheirEscrowsAreIsolated() (gas: 1304839)\n[PASS] test_ReporterCanVoidAndAllStakesAreRefunded() (gas: 570715)\n[PASS] test_SmallestRewardUnitsAndDustAreAccountedFor() (gas: 1196261)\n[PASS] test_WinnersReceiveEthAndLosersEarnHamster() (gas: 1216877)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 47.30ms (44.37ms CPU time)\n\nRan 13 tests for test/HamsterToken.t.sol:HamsterTokenTest\n[PASS] testFuzz_DelegatedTransferUsesOnlyAllowance(uint256,uint256) (runs: 512, μ: 155418, ~: 157400)\nLogs:\n  Bound result 8257\n  Bound result 6102\n\n[PASS] testFuzz_TransfersConserveSupply(uint256) (runs: 512, μ: 171841, ~: 172366)\nLogs:\n  Bound result 835632405390710433766726567\n\n[PASS] test_AllowanceCanBeReplacedRevokedAndInfinite() (gas: 268892)\n[PASS] test_ConstructorEmitsSingleMint() (gas: 9170)\n[PASS] test_DeploymentRuntimeContainsNoForbiddenOpcode() (gas: 795123)\n[PASS] test_FactoryGetsSupplyAndApplicationsNeverTakeIt() (gas: 722778)\n[PASS] test_InsufficientBalanceRevertsAndPreservesAllowance() (gas: 146244)\n[PASS] test_MetadataAndEntireSupply() (gas: 61165)\n[PASS] test_NoPrivilegedMintFreezeSeizeOrUpgrade() (gas: 680177)\n[PASS] test_RejectsNativeCurrency() (gas: 36175)\n[PASS] test_TransferWithoutApprovalAndOverspendRevert() (gas: 133305)\n[PASS] test_ZeroAddressOperationsRevert() (gas: 171937)\n[PASS] test_ZeroAndSelfTransfers() (gas: 115860)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 47.30ms (58.03ms CPU time)\n\nRan 1 test for test/HamsterInvariant.t.sol:HamsterInvariantTest\n[PASS] invariant_EscrowsConserveBothAssetsAndSupply() (runs: 128, calls: 8192, reverts: 0)\n\n╭-------------+-------------+-------+---------+----------╮\n| Contract    | Selector    | Calls | Reverts | Discards |\n+========================================================+\n| RaceHandler | advanceTime | 1099  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | bet         | 998   | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | cancel      | 1009  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | claim       | 1013  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | donate      | 1019  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | openRace    | 1051  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | reclaim     | 1022  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | settle      | 981   | 0       | 0        |\n╰-------------+-------------+-------+---------+----------╯\n\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 3\n  Bound result 3600\n  Bound result 9296\n  Bound result 299637807644260\n  Bound result 2\n  Bound result 3\n  Bound result 1\n  Bound result 25\n  Bound result 2\n  Bound result 3\n  Bound result 255\n  Bound result 4304\n  Bound result 7\n  Bound result 9541\n  Bound result 1995281824\n  Bound result 8\n  Bound result 2020\n  Bound result 1167\n  Bound result 4\n  Bound result 6400\n  Bound result 2703\n  Bound result 43200\n  Bound result 23292\n  Bound result 127\n  Bound result 5681\n  Bound result 4\n  Bound result 9436\n  Bound result 6815651440749796317\n  Bound result 6400\n  Bound result 79829455070171900517\n  Bound result 1\n  Bound result 4\n  Bound result 10018967\n  Bound result 6\n  Bound result 1\n  Bound result 557\n  Bound result 10\n  Bound result 1\n  Bound result 9\n  Bound result 8996\n  Bound result 2763728217043597703217\n  Bound result 15\n  Bound result 1220\n  Bound result 8\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.95s (1.95s CPU time)\n\nRan 6 test suites in 1.95s (2.16s CPU time): 50 tests passed, 0 failed, 0 skipped (50 total tests)\n","passed":true},{"durationMs":40,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"HamsterRaces.acceptOwnership()\",\"HamsterRaces.cancelRace(uint256)\",\"HamsterRaces.claim(uint256,address)\",\"HamsterRaces.createRace(bytes32,uint8,uint64,uint256)\",\"HamsterRaces.placeBet(uint256,uint8)\",\"HamsterRaces.reclaimRewards(uint256,address)\",\"HamsterRaces.renounceOwnership()\",\"HamsterRaces.reportResult(uint256,uint8)\",\"HamsterRaces.setResultReporter(address)\",\"HamsterRaces.transferOwnership(address)\",\"HamsterToken.approve(address,uint256)\",\"HamsterToken.transfer(address,uint256)\",\"HamsterToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":3,\"README.md\":241,\"SECURITY.md\":72,\"foundry.toml\":21,\"launch.json\":32,\"remappings.txt\":2,\"src/HamsterRaces.sol\":319,\"src/HamsterToken.sol\":14,\"test/HamsterInvariant.t.sol\":207,\"test/HamsterRaces.t.sol\":253,\"test/HamsterRacesAdversarial.t.sol\":197,\"test/HamsterRacesHedge.t.sol\":100,\"test/HamsterRacesValidation.t.sol\":259,\"test/HamsterToken.t.sol\":222,\"test/helpers/RaceTestBase.sol\":64},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"cee47de1c0bf611222bab8632809684712b373b69cf40511075cb727b9390c56","verifiedTreeHash":"5c6ab088033e20df510bebfaa80abc70b95ad010","verifierVersion":"0.1.0+a6ee6548"},{"checks":[{"durationMs":2746,"exitCode":0,"name":"build","output":"Compiling 41 files with Solc 0.8.26\nSolc 0.8.26 finished in 2.67s\nCompiler run successful!\n","passed":true},{"durationMs":18783,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 8 tests for test/HamsterRacesAdversarial.t.sol:HamsterRacesAdversarialTest\n[PASS] test_FailedRewardFundingLeavesNoRaceOrLiability() (gas: 311584)\n[PASS] test_FailedSponsorRefundCanBeRetried() (gas: 246405)\n[PASS] test_FailedTokenPayoutRollsBackAndCanBeRetried() (gas: 667971)\n[PASS] test_NativeCurrencyOutsideBetIsRejected() (gas: 41911)\n[PASS] test_ReentrantRefundCannotDrainAnotherRace() (gas: 1260979)\n[PASS] test_ReentrantWinnerCannotClaimTwice() (gas: 1058023)\n[PASS] test_RevertingRecipientDoesNotBlockSettlementOrOtherClaims() (gas: 1334950)\n[PASS] test_ShortRewardFundingRevertsAtomically() (gas: 1014008)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 118.66ms (2.27ms CPU time)\n\nRan 12 tests for test/HamsterRaces.t.sol:HamsterRacesTest\n[PASS] testFuzz_SettlementConservesBothAssets(uint96,uint96,uint96,uint96,bool) (runs: 512, μ: 1064827, ~: 1064837)\nLogs:\n  Bound result 273496690488480\n  Bound result 1068\n  Bound result 30512\n  Bound result 3\n\n[PASS] test_AnyoneCanCancelAtExactTimeout() (gas: 352263)\n[PASS] test_ClaimsCanBeDirectedByBettorAndDoNotExpire() (gas: 560452)\n[PASS] test_CreationEscrowsRewardsAndPublishesTerms() (gas: 125366)\n[PASS] test_DonationsDoNotChangeEntitlements() (gas: 640161)\n[PASS] test_EmptyRaceCanFinalizeAndReturnBudget() (gas: 122486)\n[PASS] test_NoLosingBetsReturnsStakesAndUnusedRewards() (gas: 614097)\n[PASS] test_NoWinningBetsMeansFullRefunds() (gas: 560677)\n[PASS] test_RacesAndTheirEscrowsAreIsolated() (gas: 1178099)\n[PASS] test_ReporterCanVoidAndAllStakesAreRefunded() (gas: 568799)\n[PASS] test_SmallestRewardUnitsAndDustAreAccountedFor() (gas: 1016220)\n[PASS] test_WinnersReceiveEthAndLosersEarnHamster() (gas: 1054302)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 120.44ms (124.30ms CPU time)\n\nRan 13 tests for test/HamsterRacesValidation.t.sol:HamsterRacesValidationTest\n[PASS] test_CannotAlterSettledRaceOrCancelEarlyWithoutReporter() (gas: 390309)\n[PASS] test_ClaimsRejectEarlyMissingDuplicateAndInvalidRecipient() (gas: 476170)\n[PASS] test_ConstructorNeedsNoExternalCodeButFirstUseChecksToken() (gas: 105993)\n[PASS] test_CreationRejectsUnauthorizedInvalidDuplicateAndUnfundedRaces() (gas: 583281)\n[PASS] test_DeploymentRequiresExplicitNonzeroOwnerAndToken() (gas: 6042)\n[PASS] test_EntryClosesExactlyAtDeadlineAndResultCanThenSettle() (gas: 296722)\n[PASS] test_MaximumEntryWindowAndHamsterCountAccepted() (gas: 326354)\n[PASS] test_RejectsUnknownRaceEverywhere() (gas: 173950)\n[PASS] test_RejectsZeroInvalidAndDuplicateBets() (gas: 382724)\n[PASS] test_ReporterMustBeConfiguredByOwnerBeforeCreation() (gas: 523594)\n[PASS] test_ReporterRotationAndOwnershipChangesNeverRewriteExistingTerms() (gas: 718025)\n[PASS] test_ResultRejectsUnauthorizedEarlyInvalidLateAndDuplicateCalls() (gas: 563118)\n[PASS] test_SponsorCannotTakeActiveOrOwedRewardsAndCannotReclaimTwice() (gas: 972046)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 125.48ms (4.13ms CPU time)\n\nRan 5 tests for test/HamsterRacesPayoutFuzz.t.sol:HamsterRacesPayoutFuzzTest\n[PASS] testFuzz_EveryClaimOrderPaysProportionalSharesAndEmptiesTheRace(uint8,uint256,uint256,uint256,uint256) (runs: 1000, μ: 1171079, ~: 1002687)\nLogs:\n  Bound result 2\n  Bound result 24352758972018789196568045916\n  Bound result 615773218429580186958748409024\n\n[PASS] testFuzz_ResultWindowBoundaries(uint256) (runs: 1000, μ: 503343, ~: 496220)\nLogs:\n  Bound result 1112897\n\n[PASS] testFuzz_RewardBudgetEdgesSplitExactly(uint256,uint256,uint256) (runs: 1000, μ: 1719857, ~: 1662145)\nLogs:\n  Bound result 3244530593865672566\n  Bound result 9664632935216037130\n  Bound result 7413645654577930969\n  Bound result 7525046911024080410\n  Bound result 1551505691636412270\n  Bound result 9793814574082578009\n\n[PASS] test_ReporterCannotCancelAfterSettlingAndOutsiderCannotCancelAtResolveByMinusOne() (gas: 383023)\n[PASS] test_ReportingAWinnerNobodyBackedVoidsTheRaceAndReturnsBudget() (gas: 592134)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 125.56ms (370.91ms CPU time)\n\nRan 13 tests for test/HamsterToken.t.sol:HamsterTokenTest\n[PASS] testFuzz_DelegatedTransferUsesOnlyAllowance(uint256,uint256) (runs: 512, μ: 155559, ~: 157400)\nLogs:\n  Bound result 8257\n  Bound result 6992\n\n[PASS] testFuzz_TransfersConserveSupply(uint256) (runs: 512, μ: 172180, ~: 172366)\nLogs:\n  Bound result 6\n\n[PASS] test_AllowanceCanBeReplacedRevokedAndInfinite() (gas: 268892)\n[PASS] test_ConstructorEmitsSingleMint() (gas: 9170)\n[PASS] test_DeploymentRuntimeContainsNoForbiddenOpcode() (gas: 795123)\n[PASS] test_FactoryGetsSupplyAndApplicationsNeverTakeIt() (gas: 722778)\n[PASS] test_InsufficientBalanceRevertsAndPreservesAllowance() (gas: 146244)\n[PASS] test_MetadataAndEntireSupply() (gas: 61165)\n[PASS] test_NoPrivilegedMintFreezeSeizeOrUpgrade() (gas: 680177)\n[PASS] test_RejectsNativeCurrency() (gas: 36175)\n[PASS] test_TransferWithoutApprovalAndOverspendRevert() (gas: 133305)\n[PASS] test_ZeroAddressOperationsRevert() (gas: 171937)\n[PASS] test_ZeroAndSelfTransfers() (gas: 115860)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 125.58ms (219.96ms CPU time)\n\nRan 1 test for test/HamsterInvariant.t.sol:HamsterInvariantTest\n[PASS] invariant_EscrowsConserveBothAssetsAndSupply() (runs: 128, calls: 8192, reverts: 0)\n\n╭-------------+-------------+-------+---------+----------╮\n| Contract    | Selector    | Calls | Reverts | Discards |\n+========================================================+\n| RaceHandler | advanceTime | 1099  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | bet         | 998   | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | cancel      | 1009  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | claim       | 1013  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | donate      | 1019  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | openRace    | 1051  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | reclaim     | 1022  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | settle      | 981   | 0       | 0        |\n╰-------------+-------------+-------+---------+----------╯\n\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 3\n  Bound result 3600\n  Bound result 9051\n  Bound result 299637807644260\n  Bound result 2\n  Bound result 3\n  Bound result 1\n  Bound result 25\n  Bound result 2\n  Bound result 3\n  Bound result 255\n  Bound result 4321\n  Bound result 7\n  Bound result 9542\n  Bound result 6591\n  Bound result 8\n  Bound result 19080\n  Bound result 4026611502\n  Bound result 4\n  Bound result 18\n  Bound result 585719840\n  Bound result 43200\n  Bound result 23292\n  Bound result 96\n  Bound result 3453131759529480439\n  Bound result 4\n  Bound result 9494\n  Bound result 4\n  Bound result 6815651440749796317\n  Bound result 5\n  Bound result 3453131760115200279\n  Bound result 3600\n  Bound result 10018967\n  Bound result 6\n  Bound result 1\n  Bound result 9271\n  Bound result 10\n  Bound result 6400\n  Bound result 17\n  Bound result 9027\n  Bound result 8962713102776594904861\n  Bound result 9\n  Bound result 1339\n  Bound result 6400\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.93s (1.93s CPU time)\n\nRan 1 test for test/HamsterRacesLedgerInvariant.t.sol:HamsterRacesLedgerInvariantTest\n[PASS]\nHamsterRacesLedgerInvariantTest invariants:\n[PASS] invariant_ContractHoldsExactlyWhatItOwes\n[PASS] invariant_EachRaceConservesStakesAndBudget\n[PASS] invariant_EveryPayoutMatchesItsBetsOutcome\n[PASS] invariant_NoUnauthorizedOrRepeatedActionSucceeds\n[PASS] invariant_TermsAreImmutableAndFinalStatesNeverReopen\n HamsterRacesLedgerInvariantTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭---------------+----------------+-------+---------+----------╮\n| Contract      | Selector       | Calls | Reverts | Discards |\n+=============================================================+\n| LedgerHandler | bet            | 1817  | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| LedgerHandler | cancel         | 1799  | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| LedgerHandler | claim          | 1797  | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| LedgerHandler | openRace       | 1900  | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| LedgerHandler | probe          | 1806  | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| LedgerHandler | reclaim        | 1870  | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| LedgerHandler | report         | 1749  | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| LedgerHandler | rotateReporter | 1774  | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| LedgerHandler | warp           | 1872  | 0       | 0        |\n╰---------------+----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 5000000000000000000000\n  Bound result 4\n  Bound result 10800\n  Bound result 4\n  Bound result 19\n  Bound result 2\n  Bound result 162834\n  Bound result 2\n  Bound result 29459\n  Bound result 2\n  Bound result 2441\n  Bound result 9\n  Bound result 45056\n  Bound result 7347\n  Bound result 4253804986692590097\n  Bound result 1\n  Bound result 7159\n  Bound result 10970\n  Bound result 6\n  Bound result 2\n  Bound result 17890\n  Bound result 9\n  Bound result 86400\n  Bound result 5\n  Bound result 40891\n  Bound result 116\n  Bound result 9\n  Bound result 89504\n  Bound result 10\n  Bound result 2\n  Bound result 6\n  Bound result 25\n  Bound result 6\n  Bound result 6046\n  Bound result 1\n  Bound result 21600\n  Bound result 56000\n  Bound result 12\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 18.70s (18.70s CPU time)\n\nRan 7 test suites in 18.70s (21.25s CPU time): 53 tests passed, 0 failed, 0 skipped (53 total tests)\n","passed":true},{"durationMs":48,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"HamsterRaces.acceptOwnership()\",\"HamsterRaces.cancelRace(uint256)\",\"HamsterRaces.claim(uint256,address)\",\"HamsterRaces.createRace(bytes32,uint8,uint64,uint256)\",\"HamsterRaces.placeBet(uint256,uint8)\",\"HamsterRaces.reclaimRewards(uint256,address)\",\"HamsterRaces.renounceOwnership()\",\"HamsterRaces.reportResult(uint256,uint8)\",\"HamsterRaces.setResultReporter(address)\",\"HamsterRaces.transferOwnership(address)\",\"HamsterToken.approve(address,uint256)\",\"HamsterToken.transfer(address,uint256)\",\"HamsterToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":3,\"README.md\":217,\"SECURITY.md\":69,\"foundry.toml\":21,\"remappings.txt\":2,\"src/HamsterRaces.sol\":309,\"src/HamsterToken.sol\":14,\"test/HamsterInvariant.t.sol\":201,\"test/HamsterRaces.t.sol\":230,\"test/HamsterRacesAdversarial.t.sol\":197,\"test/HamsterRacesLedgerInvariant.t.sol\":434,\"test/HamsterRacesPayoutFuzz.t.sol\":219,\"test/HamsterRacesValidation.t.sol\":259,\"test/HamsterToken.t.sol\":222,\"test/helpers/RaceTestBase.sol\":64},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"d5f8bb60c7262805b98920c056fb1e69148668ccdf52bd142ae3901f740c4dc1","verifiedTreeHash":"3978672cb375e6d8dedbae729e941941e9a96622","verifierVersion":"0.1.0+a6ee6548"},{"checks":[{"durationMs":1995,"exitCode":0,"name":"build","output":"Compiling 39 files with Solc 0.8.26\nSolc 0.8.26 finished in 1.92s\nCompiler run successful!\n","passed":true},{"durationMs":1890,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 8 tests for test/HamsterRacesAdversarial.t.sol:HamsterRacesAdversarialTest\n[PASS] test_FailedRewardFundingLeavesNoRaceOrLiability() (gas: 311584)\n[PASS] test_FailedSponsorRefundCanBeRetried() (gas: 246405)\n[PASS] test_FailedTokenPayoutRollsBackAndCanBeRetried() (gas: 667971)\n[PASS] test_NativeCurrencyOutsideBetIsRejected() (gas: 41911)\n[PASS] test_ReentrantRefundCannotDrainAnotherRace() (gas: 1260979)\n[PASS] test_ReentrantWinnerCannotClaimTwice() (gas: 1058023)\n[PASS] test_RevertingRecipientDoesNotBlockSettlementOrOtherClaims() (gas: 1334950)\n[PASS] test_ShortRewardFundingRevertsAtomically() (gas: 1014008)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 11.79ms (5.00ms CPU time)\n\nRan 13 tests for test/HamsterToken.t.sol:HamsterTokenTest\n[PASS] testFuzz_DelegatedTransferUsesOnlyAllowance(uint256,uint256) (runs: 512, μ: 155149, ~: 157400)\nLogs:\n  Bound result 3\n  Bound result 2\n\n[PASS] testFuzz_TransfersConserveSupply(uint256) (runs: 512, μ: 171705, ~: 172366)\nLogs:\n  Bound result 244\n\n[PASS] test_AllowanceCanBeReplacedRevokedAndInfinite() (gas: 268892)\n[PASS] test_ConstructorEmitsSingleMint() (gas: 9170)\n[PASS] test_DeploymentRuntimeContainsNoForbiddenOpcode() (gas: 795123)\n[PASS] test_FactoryGetsSupplyAndApplicationsNeverTakeIt() (gas: 722778)\n[PASS] test_InsufficientBalanceRevertsAndPreservesAllowance() (gas: 146244)\n[PASS] test_MetadataAndEntireSupply() (gas: 61165)\n[PASS] test_NoPrivilegedMintFreezeSeizeOrUpgrade() (gas: 680177)\n[PASS] test_RejectsNativeCurrency() (gas: 36175)\n[PASS] test_TransferWithoutApprovalAndOverspendRevert() (gas: 133305)\n[PASS] test_ZeroAddressOperationsRevert() (gas: 171937)\n[PASS] test_ZeroAndSelfTransfers() (gas: 115860)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 29.69ms (28.63ms CPU time)\n\nRan 13 tests for test/HamsterRacesValidation.t.sol:HamsterRacesValidationTest\n[PASS] test_CannotAlterSettledRaceOrCancelEarlyWithoutReporter() (gas: 390309)\n[PASS] test_ClaimsRejectEarlyMissingDuplicateAndInvalidRecipient() (gas: 476170)\n[PASS] test_ConstructorNeedsNoExternalCodeButFirstUseChecksToken() (gas: 105993)\n[PASS] test_CreationRejectsUnauthorizedInvalidDuplicateAndUnfundedRaces() (gas: 583281)\n[PASS] test_DeploymentRequiresExplicitNonzeroOwnerAndToken() (gas: 6042)\n[PASS] test_EntryClosesExactlyAtDeadlineAndResultCanThenSettle() (gas: 296722)\n[PASS] test_MaximumEntryWindowAndHamsterCountAccepted() (gas: 326354)\n[PASS] test_RejectsUnknownRaceEverywhere() (gas: 173950)\n[PASS] test_RejectsZeroInvalidAndDuplicateBets() (gas: 382724)\n[PASS] test_ReporterMustBeConfiguredByOwnerBeforeCreation() (gas: 523594)\n[PASS] test_ReporterRotationAndOwnershipChangesNeverRewriteExistingTerms() (gas: 718025)\n[PASS] test_ResultRejectsUnauthorizedEarlyInvalidLateAndDuplicateCalls() (gas: 563118)\n[PASS] test_SponsorCannotTakeActiveOrOwedRewardsAndCannotReclaimTwice() (gas: 972046)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 29.73ms (3.84ms CPU time)\n\nRan 12 tests for test/HamsterRaces.t.sol:HamsterRacesTest\n[PASS] testFuzz_SettlementConservesBothAssets(uint96,uint96,uint96,uint96,bool) (runs: 512, μ: 1064827, ~: 1064837)\nLogs:\n  Bound result 50710853051927800218\n  Bound result 65557490515453477616\n  Bound result 45206\n  Bound result 14264337593543950332\n\n[PASS] test_AnyoneCanCancelAtExactTimeout() (gas: 352263)\n[PASS] test_ClaimsCanBeDirectedByBettorAndDoNotExpire() (gas: 560452)\n[PASS] test_CreationEscrowsRewardsAndPublishesTerms() (gas: 125366)\n[PASS] test_DonationsDoNotChangeEntitlements() (gas: 640161)\n[PASS] test_EmptyRaceCanFinalizeAndReturnBudget() (gas: 122486)\n[PASS] test_NoLosingBetsReturnsStakesAndUnusedRewards() (gas: 614097)\n[PASS] test_NoWinningBetsMeansFullRefunds() (gas: 560677)\n[PASS] test_RacesAndTheirEscrowsAreIsolated() (gas: 1178099)\n[PASS] test_ReporterCanVoidAndAllStakesAreRefunded() (gas: 568799)\n[PASS] test_SmallestRewardUnitsAndDustAreAccountedFor() (gas: 1016220)\n[PASS] test_WinnersReceiveEthAndLosersEarnHamster() (gas: 1054302)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 29.87ms (33.56ms CPU time)\n\nRan 1 test for test/HamsterInvariant.t.sol:HamsterInvariantTest\n[PASS] invariant_EscrowsConserveBothAssetsAndSupply() (runs: 128, calls: 8192, reverts: 0)\n\n╭-------------+-------------+-------+---------+----------╮\n| Contract    | Selector    | Calls | Reverts | Discards |\n+========================================================+\n| RaceHandler | advanceTime | 1099  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | bet         | 998   | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | cancel      | 1009  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | claim       | 1013  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | donate      | 1019  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | openRace    | 1051  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | reclaim     | 1022  | 0       | 0        |\n|-------------+-------------+-------+---------+----------|\n| RaceHandler | settle      | 981   | 0       | 0        |\n╰-------------+-------------+-------+---------+----------╯\n\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 3\n  Bound result 3600\n  Bound result 9051\n  Bound result 299637807644260\n  Bound result 2\n  Bound result 3\n  Bound result 1\n  Bound result 25\n  Bound result 2\n  Bound result 3\n  Bound result 255\n  Bound result 4321\n  Bound result 7\n  Bound result 9542\n  Bound result 6591\n  Bound result 8\n  Bound result 19080\n  Bound result 6400\n  Bound result 2033\n  Bound result 7428\n  Bound result 1\n  Bound result 43200\n  Bound result 23292\n  Bound result 127\n  Bound result 2004\n  Bound result 9488\n  Bound result 6815651440749796317\n  Bound result 6400\n  Bound result 299637807657219\n  Bound result 1\n  Bound result 4\n  Bound result 10018967\n  Bound result 6\n  Bound result 1\n  Bound result 9269\n  Bound result 10\n  Bound result 1\n  Bound result 9\n  Bound result 9007\n  Bound result 8981159846850304451024\n  Bound result 3\n  Bound result 257\n  Bound result 8\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.79s (1.79s CPU time)\n\nRan 5 test suites in 1.79s (1.89s CPU time): 47 tests passed, 0 failed, 0 skipped (47 total tests)\n","passed":true},{"durationMs":49,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"HamsterRaces.acceptOwnership()\",\"HamsterRaces.cancelRace(uint256)\",\"HamsterRaces.claim(uint256,address)\",\"HamsterRaces.createRace(bytes32,uint8,uint64,uint256)\",\"HamsterRaces.placeBet(uint256,uint8)\",\"HamsterRaces.reclaimRewards(uint256,address)\",\"HamsterRaces.renounceOwnership()\",\"HamsterRaces.reportResult(uint256,uint8)\",\"HamsterRaces.setResultReporter(address)\",\"HamsterRaces.transferOwnership(address)\",\"HamsterToken.approve(address,uint256)\",\"HamsterToken.transfer(address,uint256)\",\"HamsterToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":3,\"README.md\":217,\"SECURITY.md\":69,\"foundry.toml\":21,\"remappings.txt\":2,\"src/HamsterRaces.sol\":309,\"src/HamsterToken.sol\":14,\"test/HamsterInvariant.t.sol\":201,\"test/HamsterRaces.t.sol\":230,\"test/HamsterRacesAdversarial.t.sol\":197,\"test/HamsterRacesValidation.t.sol\":259,\"test/HamsterToken.t.sol\":222,\"test/helpers/RaceTestBase.sol\":64},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":943,"exitCode":0,"name":"slither","output":"[high/medium] reentrancy-balance at src/HamsterRaces.sol:136: Reentrancy in HamsterRaces.createRace(bytes32,uint8,uint64,uint256) (src/HamsterRaces.sol#136-175):\n[low/medium] missing-zero-check at src/HamsterRaces.sol:227: HamsterRaces.claim(uint256,address).recipient (src/HamsterRaces.sol#227) lacks a zero-check on :\n[low/medium] timestamp at src/HamsterRaces.sol:178: HamsterRaces.placeBet(uint256,uint8) (src/HamsterRaces.sol#178-191) uses timestamp for comparisons\n[low/medium] timestamp at src/HamsterRaces.sol:217: HamsterRaces.cancelRace(uint256) (src/HamsterRaces.sol#217-222) uses timestamp for comparisons\n[low/medium] timestamp at src/HamsterRaces.sol:196: HamsterRaces.reportResult(uint256,uint8) (src/HamsterRaces.sol#196-213) uses timestamp for comparisons\n[low/medium] timestamp at src/HamsterRaces.sol:136: HamsterRaces.createRace(bytes32,uint8,uint64,uint256) (src/HamsterRaces.sol#136-175) uses timestamp for comparisons","passed":true},{"durationMs":337,"exitCode":0,"name":"aderyn","output":"[low] centralization-risk at src/HamsterRaces.sol:14: Centralization Risk (3 places)\n[low] large-numeric-literal at src/HamsterToken.sol:9: Large Numeric Literal\n[low] local-variable-shadowing at src/HamsterRaces.sol:54: Local Variable Shadows State Variable","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"f5a54aa78be3b4ebad0c5dd0a9df886dfe414c84680c21047e1ce4194062de76","verifiedTreeHash":"07f2130964d8857f8aab65bad0aef39b4e6a2b6b","verifierVersion":"0.1.0+a6ee6548"}]}