{"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":"a95d8284-6bc6-479d-ac62-7e115d14a150","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"264f4dc5161ff819a3f6cfb1848a5bca3d1823e4e7a79b3f34691d8ed5b86300","dependsOn":["audit_imported_code"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"2d7d84a1a9972f1497e5bc68a704cbf42294d08c62d2868f102ffe3b909b9b51","skillId":"adapt-contract-project","tools":[]},"key":"adapt_contract_project","kind":"code","role":"implement","skillHash":"2d7d84a1a9972f1497e5bc68a704cbf42294d08c62d2868f102ffe3b909b9b51","skillId":"adapt-contract-project","state":"accepted"},{"acceptedSubmissionHash":"114744ad6e330f1236a900d674e3efb1871cec5e410d6929e317014728636766","dependsOn":["adapt_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":"555819c89dfa04d4032926ebc38c2d7b9fb5fdd3bb6abe9a3026417aa892aa47","dependsOn":["adapt_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":"08bbb136d85bb38bc717df12c7804c623600fa2178e194bc41c2c042535df745","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"9897597976b0e72440db7ca402d225aea20f3d37205ba53e7df2400ff80f29cc","skillId":"audit-imported-code","tools":[]},"key":"audit_imported_code","kind":"code","role":"review","skillHash":"9897597976b0e72440db7ca402d225aea20f3d37205ba53e7df2400ff80f29cc","skillId":"audit-imported-code","state":"accepted"},{"acceptedSubmissionHash":"9a9a066a1a4df0d55b8cbfd82c0cbb14c02cee824a1f78afa6457664fb9b9ed4","dependsOn":["adapt_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":"03582182ee29b323e27ca80503bcfc860a71a85e44a6ca0cf1c43779ee2a7ab1","dependsOn":["adapt_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":"cf2c44e2a565d5a4f723382e87513d8d5a567ca7752cb853ab63a2d356d01eba","dependsOn":["adapt_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":"bf291c2aa3a5d321315551c5f13364ecadb7c670b258b656f492c3976b85009d","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"da098f0b9c1095d1126a06ee77996ac0384f8127dd0c9e8271616e25813f1372","dependsOn":["adapt_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":"Deploy the GAMMA game contracts from this repository exactly as written, without changing their logic. Foundry project at the repository root. Deploy, in order: (1) DrandBeacon (src/DrandBeacon.sol, no constructor arguments), a permissionless verifier of drand evmnet BLS signatures on BN254. (2) GammaGame (src/GammaGame.sol) with constructor (poolManager 0x8366a39cc670b4001a1121b8f6a443a643e40951, imd 0x5f7bb59365ce557c26dbcaa4ee9d39a4b95b7127, beacon $contract:DrandBeacon, configurator $owner). GammaGame is the proof-of-play mining game for the GAMMA/IMD pool: players buy energies with IMD (bought GAMMA is burned), four machines with 10/30/60/90% fail rates, a fixed 800M GAMMA Reserve released every 10 minutes, survivors split the Shares, failures split the evening Harvest, claim fee 80% to 10% over 24h split burn/unclaimed bonus/Harvest. No owner, no mint, no withdraw; the configurator can only call initialize once. No backend signer. This repository already contains the fixes from launch 2210's audit (see ADAPTATION.md). By design: randomness is drand only, with no block-data fallback, because any fallback gives the settler a choice between two public draws (a late drand round is always provable, so an outage only delays settlement); and hook fees arrive only in GAMMA (the hook refuses exact-output buys), so the game never holds or swaps IMD.","parentJobId":null,"planHash":"656fa855c691a833419e78944ee43f54b90681dbd6bbc1742115f7e456c485d4","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"a95d8284-6bc6-479d-ac62-7e115d14a150","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-2215-src-drandbeacon-sol-src-gammagame-sol"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51881","feedbackHash":"faec5cb3df3aa04736130d61ccf165a21ad05079171c24ec6ef054a624f62157","nodeKey":"adapt_contract_project","submissionHash":"264f4dc5161ff819a3f6cfb1848a5bca3d1823e4e7a79b3f34691d8ed5b86300","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"54235","feedbackHash":"2957cbd3f02672d5b1b093f915c392b79de2e2f8b06be8a8c3fae4eab804eb4e","nodeKey":"audit_economics","submissionHash":"114744ad6e330f1236a900d674e3efb1871cec5e410d6929e317014728636766","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51442","feedbackHash":"3a8d67ad7ec3a030a6749ba5fefb67fac3da015d998c6adeb5253383240b8089","nodeKey":"audit_flow","submissionHash":"555819c89dfa04d4032926ebc38c2d7b9fb5fdd3bb6abe9a3026417aa892aa47","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52605","feedbackHash":"6ed9e3405f26f109ea706cc1f694f27ca0c1ddcd1ce96bf9290eaeaa48605bba","nodeKey":"audit_imported_code","submissionHash":"08bbb136d85bb38bc717df12c7804c623600fa2178e194bc41c2c042535df745","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52676","feedbackHash":"3a8c89a1d705a4836dd6aa52a85633e15e558fbdde2c3cf28511162387463153","nodeKey":"audit_judge","submissionHash":"9a9a066a1a4df0d55b8cbfd82c0cbb14c02cee824a1f78afa6457664fb9b9ed4","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"54234","feedbackHash":"c7abe57bb1e40369f5f9784010de203b0882cecfb0d52717c233b4af07805546","nodeKey":"audit_math","submissionHash":"03582182ee29b323e27ca80503bcfc860a71a85e44a6ca0cf1c43779ee2a7ab1","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"54236","feedbackHash":"6773aa06bda459de83aa0d9b318f58ca069ee6cd23feaae2f4632744846eb838","nodeKey":"audit_permissions","submissionHash":"cf2c44e2a565d5a4f723382e87513d8d5a567ca7752cb853ab63a2d356d01eba","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51489","feedbackHash":"74f0ad05a9fda06d7c0c1ad193e0df30278388ffc0aff893fe944a4a33b87721","nodeKey":"manifest","submissionHash":"bf291c2aa3a5d321315551c5f13364ecadb7c670b258b656f492c3976b85009d","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52135","feedbackHash":"b1d5fe6342ba4abc528c1569ff5be98f5c5a86a3eadf230d1fec641baa76a27f","nodeKey":"write_foundry_tests","submissionHash":"da098f0b9c1095d1126a06ee77996ac0384f8127dd0c9e8271616e25813f1372","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"e0c733063b6a472e40df57810436ea87ab013e344392c72f79b658d396e4aaed","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"2042b7cb7b952e30","findings":[{"citation":"resolved","description":"released(n) for n < YEAR1_EPOCHS divides by (RAY - K_YEAR1), where K_YEAR1 is documented as K^YEAR1_EPOCHS. The on-chain exponentiation FixedPointMathLib.rpow(K, 52560, RAY) returns 121441492644200733592905837, i.e. 5,339 units (1e-27 scale) less than the hard-coded constant, because rpow truncates at every squaring step while the constant was computed exactly. The denominator is therefore 5,339e-27 smaller than the one the running code's own K^n reaches, so every year-one released(n) is scaled up by about 6.1e-24 relative, at most ~3,000 wei on the 500,000,000e18 year total. The year-one/tail seam is unaffected: released(52560) is taken from the tail branch and equals YEAR1 exactly, released(52559) stays below it by about 4.9e22 wei, monotonicity and the telescoping of between() hold (existing testFuzz_monotonic, testFuzz_betweenAdds, test_year1IsExactly500M pass). Reported for completeness of the Math Precision pass: no funds are at risk and no behaviour changes beyond wei-level dust that stays inside the Reserve.","line":21,"path":"src/libraries/GammaEmission.sol","reproduction":"Call FixedPointMathLib.rpow(GammaEmission.K, GammaEmission.YEAR1_EPOCHS, GammaEmission.RAY) in a Foundry test and compare with GammaEmission.K_YEAR1. Expected (per the comment on line 20): equal. Actual: 121441492644200733592905837 != 121441492644200733592911176 (assertEq fails). Consequence: GammaEmission.released(52559) is ~3e3 wei higher than the formula with the exact rpow value would give; released(52560) == 500_000_000e18 regardless.","severity":"info","snippet":"    uint256 internal constant K_YEAR1 = 121441492644200733592911176;","title":"K_YEAR1 constant is not the value rpow(K, YEAR1_EPOCHS, RAY) actually produces (5,339e-27 too high)"}],"hash":"03582182ee29b323e27ca80503bcfc860a71a85e44a6ca0cf1c43779ee2a7ab1","nodeId":"9a5baa7b-1aa6-455a-9b12-17baa9f7202d","outcome":"completed","summary":"The findings file is in place at the repository root with 1 finding and 21 coverage rows, the stray copy is deleted, and the scratch tests are removed. The working tree holds no changes beyond the findings file.\n\n**Outcome.** The math area of the GAMMA game holds. I traced every division, cast, subtraction and index product in the Math Precision, Boundary and Numerical Gap checklists against the live code and found no defect that moves funds or breaks a guarantee. The one item reported is informational.\n\n**What I checked and confirmed sound:**\n- Claim fee split. Burn, bonus and harvest parts sum exactly to the fee plus the claimant's self share. The self index delta is always a subset of the global delta, so the bonus subtraction cannot underflow.\n- Bonus index scale. Each unclaimed batch's winnings are bounded by the unclaimed total at every step, so index products stay under 1e63 and cannot overflow.\n- Settlement arithmetic. Empty-epoch runs always advance, stop at day boundaries, and the emission telescopes exactly. The 60/20/20/0 share deal sums to the shares pool with dust to the Harvest. Per-fail remainders roll to the next day.\n- Survivor draw. The 16-bit draw maps to fail probabilities within 0.001 percentage points of 10/30/60/90 percent.\n- Boundaries. Zero-amount donate, settle and claim are no-ops. Casts to uint16 and uint32 are bounded by the batch cap. The exact-output swap requires the full GAMMA amount and bounds IMD spent. Held GAMMA can never exceed the balance, so sweep cannot underflow.\n- Beacon input. Signatures must be exactly 64 bytes and non-reduced coordinates fail the pairing precompile, so a round has one encoding and one randomness.\n\n**The one finding (info).** The hard-coded `K_YEAR1` constant in the emission library is 5,339 units at 1e-27 scale above what the on-chain `rpow` actually produces for K raised to the year-one epoch count. The effect is at most a few thousand wei across the 500M year-one release, year one still ends at exactly 500M, and monotonicity holds. No fix is needed for safety.\n\n**Checks run.** Foundry build passed. The emission, claim-bonus and settlement-properties suites passed (31 tests). A scratch probe confirmed the constant mismatch and the lone-claim accounting; it was removed afterward.\n\n**Known advisories not re-reported.** The claim fee clock measured from day end rather than harvest time, wei-level bonus dust, and off-chain drand knowledge under a lagging chain clock are already documented as accepted in the adaptation notes.","treeHash":null,"usage":{"cachedInputTokens":1176650,"inputTokens":356,"model":"claude-fable-5-1","outputTokens":34857,"runtime":"claude","turns":23,"wallClockMs":1288944}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"c79042525fc4a50d","findings":[{"citation":"resolved","description":"_claim subtracts the claimant's winnings from totalUnclaimed before distributing the bonus third of the fee, with the stated intent that nobody earns a bonus from their own claim. The exclusion only covers batches claimed in the same call. A player with several harvested batches claims them one call at a time (even in the same block): the bonus third of each earlier call's fee is distributed over totalUnclaimed, which still contains the player's remaining batches, and the next call pays it back to the same player as `bonus`. With a dominant unclaimed position the claimant recovers almost the whole bonus third, so the 80% early-exit fee becomes about 53% in the limit, and the other unclaimed holders, whom the README says the bonus is for, receive correspondingly less. The guarantee the code comment states does not hold.","line":471,"path":"src/GammaGame.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {MockERC20} from \"solmate/src/test/utils/mocks/MockERC20.sol\";\nimport {ERC20} from \"solmate/src/tokens/ERC20.sol\";\nimport {PoolManager} from \"v4-core/src/PoolManager.sol\";\nimport {IPoolManager} from \"v4-core/src/interfaces/IPoolManager.sol\";\nimport {IHooks} from \"v4-core/src/interfaces/IHooks.sol\";\nimport {Hooks} from \"v4-core/src/libraries/Hooks.sol\";\nimport {TickMath} from \"v4-core/src/libraries/TickMath.sol\";\nimport {PoolKey} from \"v4-core/src/types/PoolKey.sol\";\nimport {Currency} from \"v4-core/src/types/Currency.sol\";\nimport {ModifyLiquidityParams} from \"v4-core/src/types/PoolOperation.sol\";\nimport {PoolModifyLiquidityTest} from \"v4-core/src/test/PoolModifyLiquidityTest.sol\";\n\nimport {GammaGame} from \"src/GammaGame.sol\";\nimport {GammaHook} from \"src/GammaHook.sol\";\nimport {GammaToken} from \"src/GammaToken.sol\";\nimport {IGammaBeacon} from \"src/interfaces/IGammaBeacon.sol\";\n\ncontract ProofBeacon is IGammaBeacon {\n    mapping(uint64 => bytes32) public randomness;\n\n    function genesis() external pure returns (uint64) {\n        return 1727521075;\n    }\n\n    function period() external pure returns (uint64) {\n        return 3;\n    }\n\n    function set(uint64 round, bytes32 value) external {\n        randomness[round] = value;\n    }\n\n    function prove(uint64 round, bytes calldata signature) external returns (bytes32) {\n        if (randomness[round] == bytes32(0)) randomness[round] = keccak256(signature);\n        return randomness[round];\n    }\n}\n\n/// @notice GammaGame._claim removes the claimant's winnings from `totalUnclaimed` before sharing the fee \"so nobody\n/// earns a bonus from their own claim\", but only within one call. Claiming batch by batch, the claimant's remaining\n/// unclaimed batches receive the bonus third of each earlier claim's fee. This test fails while a split claim pays\n/// more than the same batches claimed together in the same block.\ncontract Proof_SplitClaimBonus is Test {\n    uint160 constant SQRT_PRICE_1_1 = 79228162514264337593543950336;\n    uint24 constant LP_FEE = 12_500;\n    int24 constant TICK_SPACING = 60;\n\n    PoolManager manager;\n    PoolModifyLiquidityTest lpRouter;\n    MockERC20 imd;\n    GammaToken gamma;\n    ProofBeacon beacon;\n    GammaGame game;\n    PoolKey key;\n\n    address alice = makeAddr(\"alice\");\n    address bob = makeAddr(\"bob\");\n    GammaGame.BeaconProof[] noProofs;\n\n    function setUp() public {\n        vm.warp(1_760_000_000);\n        manager = new PoolManager(address(this));\n        lpRouter = new PoolModifyLiquidityTest(manager);\n        imd = new MockERC20(\"Identity.md\", \"IMD\", 18);\n        beacon = new ProofBeacon();\n        gamma = new GammaToken(address(this));\n        game = new GammaGame(IPoolManager(address(manager)), ERC20(address(imd)), beacon, address(this));\n\n        uint160 flags = Hooks.BEFORE_INITIALIZE_FLAG | Hooks.AFTER_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG\n            | Hooks.AFTER_SWAP_FLAG | Hooks.BEFORE_SWAP_RETURNS_DELTA_FLAG | Hooks.AFTER_SWAP_RETURNS_DELTA_FLAG;\n        address hookAddr = address(uint160(0x4444 << 144) | flags);\n        deployCodeTo(\"GammaHook.sol:GammaHook\", abi.encode(address(manager), address(imd), address(game)), hookAddr);\n\n        bool gammaIs0 = address(gamma) < address(imd);\n        key = PoolKey({\n            currency0: Currency.wrap(gammaIs0 ? address(gamma) : address(imd)),\n            currency1: Currency.wrap(gammaIs0 ? address(imd) : address(gamma)),\n            fee: LP_FEE,\n            tickSpacing: TICK_SPACING,\n            hooks: IHooks(hookAddr)\n        });\n        manager.initialize(key, SQRT_PRICE_1_1);\n\n        gamma.transfer(address(game), game.RESERVE());\n        imd.mint(address(this), 1_000_000_000e18);\n        gamma.approve(address(lpRouter), type(uint256).max);\n        imd.approve(address(lpRouter), type(uint256).max);\n        lpRouter.modifyLiquidity(\n            key,\n            ModifyLiquidityParams({\n                tickLower: TickMath.minUsableTick(TICK_SPACING),\n                tickUpper: TickMath.maxUsableTick(TICK_SPACING),\n                liquidityDelta: 100_000_000e18,\n                salt: 0\n            }),\n            \"\"\n        );\n        game.initialize(key);\n\n        address[2] memory players = [alice, bob];\n        for (uint256 i; i < 2; ++i) {\n            imd.mint(players[i], 50_000_000e18);\n            vm.prank(players[i]);\n            imd.approve(address(game), type(uint256).max);\n        }\n        vm.warp(block.timestamp + 97 minutes);\n    }\n\n    function buy(address who, uint8 machine, uint256 energies) internal returns (uint256 id) {\n        vm.prank(who);\n        id = game.buyEnergies(machine, energies, energies * 2_000e18, noProofs);\n    }\n\n    function settleAll() internal {\n        uint256 n = game.currentEpoch();\n        for (uint256 i = game.activeCursor(); i < game.activeEpochCount(); ++i) {\n            uint256 e = game.activeEpochs(i);\n            if (e >= n) break;\n            uint64 round = game.roundForEpoch(e);\n            if (beacon.randomness(round) == bytes32(0)) beacon.set(round, keccak256(abi.encode(\"drand\", round)));\n        }\n        for (uint256 i; i < 200; ++i) {\n            uint256 before = game.cursor();\n            game.settle(256, noProofs);\n            if (game.cursor() == before && game.cursor() >= game.currentEpoch()) break;\n        }\n    }\n\n    function ids1(uint256 a) internal pure returns (uint256[] memory r) {\n        r = new uint256[](1);\n        r[0] = a;\n    }\n\n    function test_splitClaimMustNotPayMoreThanOneClaim() public {\n        uint256 a1 = buy(alice, 3, 500);\n        uint256 a2 = buy(alice, 3, 500);\n        buy(bob, 3, 10);\n        vm.warp(game.dayEnd(0) + 1);\n        settleAll();\n        assertTrue(game.getDay(0).finalized, \"day 0 harvested\");\n\n        uint256 snap = vm.snapshotState();\n        uint256[] memory both = new uint256[](2);\n        both[0] = a1;\n        both[1] = a2;\n        vm.prank(alice);\n        uint256 paidTogether = game.claim(both);\n        vm.revertToState(snap);\n\n        vm.prank(alice);\n        uint256 p1 = game.claim(ids1(a1));\n        vm.prank(alice);\n        uint256 p2 = game.claim(ids1(a2));\n\n        emit log_named_uint(\"paid in one call\", paidTogether);\n        emit log_named_uint(\"paid in two calls\", p1 + p2);\n        assertLe(p1 + p2, paidTogether, \"splitting a claim earned the claimant a bonus from their own fee\");\n    }\n}","reproduction":"State: fixture pool (GAMMA/IMD, 1:1, 100M liquidity), alice buys two batches of 500 energies on machine 3 (Air) in epoch 0, bob buys 10 on Air; warp to dayEnd(0)+1 and settle so day 0 is harvested; the 80% fee applies. (1) alice calls claim([a1,a2]) once: payout 649,073,294,928,695,075,181,214 wei. (2) From the same state alice calls claim([a1]) then claim([a2]) in the same block: payouts sum to 1,072,838,170,179,914,773,281,780 wei, 423,764 GAMMA more. The difference is the bonus third of the first claim's fee (1,296,720 GAMMA * 1/3, minus bob's pro-rata share) flowing back to alice via bonusIndex. Expected: the same batches claimed in one or several calls pay the same; actual: the split pays 65% more. Fix direction: compute the bonus share excluding all of the claimant's unclaimed winnings (per-owner unclaimed accounting), or drop the claim that a claimant cannot earn from their own fee and document the behaviour.","severity":"medium","snippet":"        // Remove our winnings before sharing the fee, so nobody earns a bonus from their own claim.\n        totalUnclaimed -= winningsTotal;","title":"Splitting a claim into several calls lets the claimant collect the bonus third of their own claim fee"},{"citation":"resolved","description":"The seed of epoch e is the drand round at epochEnd(e)+6 s. Purchases into e are accepted while block.timestamp < epochEnd(e). The check at line 222 only rejects a purchase when the round is already stored in the beacon, but a buyer who has seen the round off-chain (the sequencer's timestamp lagging real time by at least 7 s, or the buyer's transaction being sequenced into a block whose timestamp is older than real time by that much) simply does not prove it, computes keccak256(abi.encode(r, e)), simulates drawSurvivors(seed, batchCount(), n, failBps(m)) and dealShares(seed), and buys only when the result is favourable (the next batchId is predictable when no other purchase lands first). On Arbitrum Nitro chains like Robinhood Chain the sequencer sets block timestamps; they are normally within a second of real time, so this needs a lagging or recovering sequencer. The requester lists this as advisory 5f3b7fff with BEACON_DELAY kept at 6 s; it is restated here with the concrete state so the adapter can decide whether a larger delay is acceptable.","line":222,"path":"src/GammaGame.sol","reproduction":"State: block.timestamp = epochEnd(e) - 1 while drand round roundForEpoch(e) is already public off-chain (chain clock >= 7 s behind). Buyer computes seed = keccak256(abi.encode(randomness, e)) and checks game.drawSurvivors(seed, game.batchCount(), 1, 9000) == 1; if not, buyer waits for a later epoch. When it is 1, buyer calls buyEnergies(3, 1, maxImd, []) which passes because beacon.randomness(round) == 0. Anyone then proves the round and settles: the batch on the 90%-fail machine has survivors == 1 with certainty, versus a 10% chance for an honest buyer. test/scratch/Review.t.sol::test_offchainKnownSeedBeforeEpochCloses reproduces this with the fixture's mock beacon (passes, showing the draw is chosen).","severity":"low","snippet":"        if (beacon.randomness(roundForEpoch(e)) != bytes32(0)) revert EpochSeedKnown();","title":"EpochSeedKnown only blocks seeds proven on-chain; a buyer who knows the drand round off-chain can still pick a winning draw if the chain clock lags drand by 7 seconds or more"},{"citation":"resolved","description":"claimFeeBps(d) measures elapsed time from dayEnd(d). The Harvest of day d happens only when settlement reaches epoch (d+1)*144, which needs every active epoch's drand proof and enough settle units; nothing settles unless someone buys, claims or calls settle. If the Harvest lands 24 h or more after dayEnd(d) (a drand outage, or simply nobody settling), the first claim of that day already pays the 10% floor, and the burn, bonus and next Harvest lose the 80%-to-10% schedule the README promises ('80% right after the Harvest'). The requester lists this as advisory bb6f63d5/b447e15e ('the claimant gains nothing over waiting 24h'); that is true for the claimant but the protocol receives less than the schedule says. Restated with the concrete state.","line":565,"path":"src/GammaGame.sol","reproduction":"State: batches bought on day 0; no call to settle, buyEnergies or claim between dayEnd(0) and dayEnd(0) + 1 day (or the day's last drand round is proven only then). At block.timestamp = dayEnd(0) + 1 day + 1 anyone calls settle(256, proofs): day 0 is harvested now. The owner immediately calls claim([id]): claimFeeBps(0) returns CLAIM_FEE_END_BPS = 1000 (elapsed >= DAY), so the fee is 10% in the same block as the Harvest. Expected per README: 80% right after the Harvest, falling to 10% over the next 24 h.","severity":"low","snippet":"        uint256 end = dayEnd(d);\n        if (block.timestamp <= end) return CLAIM_FEE_START_BPS;\n        uint256 elapsed = block.timestamp - end;\n        if (elapsed >= DAY) return CLAIM_FEE_END_BPS;","title":"The claim fee decays from the day's end, not from its Harvest, so a late settlement removes the 80% early-claim fee"},{"citation":"resolved","description":"This is the requester's stated design (drand only, no block-data fallback, no owner), and the audit agrees the fallback was worse. It is recorded so the risk is explicit: there is no second source and no admin, so the contract has no way to move past an epoch whose drand round is never signed. The beacon's parameters were checked against the live network on 2026-10-19 (api.drand.sh evmnet info: public key, genesis 1727521075, period 3 all match src/DrandBeacon.sol; the live signature of round 21622006 verifies on the contract and is rejected for round 21622007, test/scratch/Review.t.sol::test_liveRoundVerifies). Group resharing keeps the drand public key, so only a shutdown or key change of evmnet triggers this.","line":366,"path":"src/GammaGame.sol","reproduction":"State: epoch e has at least one batch, cursor == e, and no valid signature for roundForEpoch(e) ever exists (evmnet shut down or re-keyed). Every settle/buy/claim hits `seed == 0 -> break`; cursor never advances past e; _harvest for day e/144 and all later days never runs; claim() for every batch of day >= e/144 returns 0 forever; the undistributed Reserve (RESERVE minus released(e)) and all winnings of unharvested days stay in the contract with no function able to move them. Expected under the requester's design: delay only; actual under this state: permanent.","severity":"low","snippet":"            if (seed == bytes32(0)) break; // waiting for the epoch's drand round to be proven","title":"Single external dependency: if drand evmnet stops publishing, every later epoch, Harvest and claim is frozen with no recovery path"},{"citation":"resolved","description":"gammaHeld is the only accounting of GAMMA the game can pay out; it rises only in initialize, donate and a hook fee that was actually received. GAMMA sent with a plain transfer after initialize raises the balance but no liability, so no function ever pays it out, and totalBurned does not include it either. Frontends that mistake the game for a donation address lose the tokens. A one-line sweep `donate`-style of (balanceOf - gammaHeld) into today's Harvest would recover it; otherwise document that only donate() funds the Harvest.","line":131,"path":"src/GammaGame.sol","reproduction":"State: initialized game. A user calls gamma.transfer(address(game), 1_000e18). gammaHeld is unchanged, getDay(today()).pot is unchanged, totalBurned is unchanged; claim() payouts are computed from pps/perFail only, so the 1,000 GAMMA can never be paid to anyone. Expected: either rejected or credited to the Harvest; actual: silently stranded.","severity":"low","snippet":"    uint256 public gammaHeld;","title":"GAMMA transferred directly to the game is stranded and not counted as burned"},{"citation":"resolved","description":"beforeInitialize only requires the pool to contain IMD and afterInitialize binds the hook to whichever pool is initialized first. PoolManager.initialize is permissionless, so between the hook's deployment and the real GAMMA/IMD pool's initialization anyone can initialize {FakeToken, IMD, hook}; the real pool then reverts PoolAlreadySet, and GammaGame.initialize rejects the real key (poolId mismatch) and the fake key (ReserveNotFunded). The Reserve is not lost (the configurator can deploy another hook bound to this game and a fresh pool), but the planned pool and its liquidity step fail. ADAPTATION.md says the hook launch creates hook and pool in one transaction, which closes this; the hook is outside this manifest (the launch deploys only DrandBeacon and GammaGame). Recorded for the hook launch's reviewer; no fix needed if atomic creation is enforced.","line":88,"path":"src/GammaHook.sol","reproduction":"State: GammaHook deployed at a flag-valid address, launchedAt == 0, real pool not yet initialized. Attacker deploys FakeToken and calls poolManager.initialize(PoolKey{currency0/1 = sorted(FakeToken, IMD), fee: any, tickSpacing: any, hooks: hook}, sqrtPrice). beforeInitialize passes (one side is IMD), afterInitialize sets poolId to the fake key and launchedAt. The honest poolManager.initialize(realKey) now reverts PoolAlreadySet, and game.initialize(realKey) reverts BadPool at the poolId check. Expected: hook binds only to the GAMMA/IMD pool; actual: first IMD pool wins.","severity":"low","snippet":"        if (launchedAt != 0) revert PoolAlreadySet();\n        if (Currency.unwrap(key.currency0) != imd && Currency.unwrap(key.currency1) != imd) revert NotImdPair();","title":"GammaHook binds to the first IMD pool initialized with it, so a front-runner can bind it to a fake pair (hook is not in this manifest)"},{"citation":"resolved","description":"Read in full: src/GammaGame.sol, src/DrandBeacon.sol, src/GammaHook.sol, src/GammaToken.sol, src/libraries/GammaEmission.sol, src/interfaces/{IGammaBeacon,IGammaGame,IGammaHook}.sol, src/vendor/bls/BLS.sol (signature parsing and validation paths), test/utils/GammaFixture.sol, test/DrandBeaconAdversarial.t.sol, test/GammaEmission.t.sol, ADAPTATION.md, README.md, launch.json, and the two pinned protected tests. Not read line by line: src/vendor/bls/ModExp.sol and Precompiles.sol (vendored, exercised by the real-signature tests), script/, frontend/, launch/. Could not be reached: the live PoolManager (0x8366a39c...) and IMD token (0x5f7bb593...) on Robinhood Chain (chain 4663); their behaviour (standard ERC-20 without transfer fees, v4 PoolManager ABI) is assumed, and buyEnergies would revert rather than lose funds if IMD took a transfer fee. Checks run: forge build clean (solc 0.8.26, via-IR); forge test excluding the invariant suites: 99 passed, 0 failed; the two invariant suites (GammaInvariant, GammaAccountingInvariant: solvent, supplyNeverGrows, cursorNeverAhead, gammaHeldMatchesBalance, cashFlowsConserveGamma, everyLiabilityIsBacked, supplyAndBatchAggregates) also pass with the default 64 runs x depth 64. Reviewed against the Pashov checklist: every state-changing entry point (initialize, buyEnergies, claim, claimMatured, settle, donate, onHookFee, unlockCallback, prove) for access, reentrancy, accounting and rounding: no owner, mint, withdraw, pause, proxy, delegatecall or selfdestruct; constructors are nonpayable, make no external calls and do not use msg.sender; launch.json has exactly kind/contracts/notes, two uniquely named contracts in dependency order, one string per constructor parameter matching the brief's addresses, $contract:DrandBeacon and $owner, and notes under 4,000 characters. drand evmnet parameters match the live network and a live round verifies (see the drand finding). Settlement accounting (tranche telescoping, Shares/Harvest split, perFail, bonus index, gammaHeld) was traced by hand and found consistent; no loss-of-funds path was found in the two contracts this launch deploys.","line":25,"path":"src/GammaGame.sol","reproduction":"Not a defect. Commands: forge build; forge test --no-match-contract Invariant; forge test --match-path test/scratch/Review.t.sol -vv; forge test --match-path test/scratch/Proof_SplitClaimBonus.t.sol (fails on current code as the medium finding states).","severity":"info","snippet":"contract GammaGame is IGammaGame, IUnlockCallback, ReentrancyGuard {","title":"Scope: contracts read, checks run and what could not be reached"}],"hash":"08bbb136d85bb38bc717df12c7804c623600fa2178e194bc41c2c042535df745","nodeId":"ae414198-3c28-4c0e-ad4d-83a69cc707cc","outcome":"completed","summary":"Review complete. The findings file is written and validated, and the repository tree is otherwise untouched.\n\n**Outcome.** No loss-of-funds path was found in the two contracts this launch deploys. The one substantive defect is a medium in the claim fee's bonus accounting. The drand beacon was checked against the live network and works.\n\n**Findings written to `.imd-findings.json`** (7 entries, one with a proof):\n\n- **Medium, split claims recover the claimant's own fee bonus.** The code removes a claimant's winnings from the bonus pool only within one call. Claiming two batches in two calls paid 1,072,838 GAMMA against 649,073 for the same batches in one call. The proof test fails on the current code for that reason.\n- **Low, off-chain known seed.** A buyer who sees the drand round before the chain clock reaches the epoch's end can simulate the draw and buy only when it wins. Needs the sequencer clock to lag drand by 7 seconds or more. This is the requester's documented advisory, restated with the triggering state.\n- **Low, claim fee decays from day end, not from the Harvest.** A Harvest that lands 24 hours late is claimable at the 10% floor immediately. Also a documented advisory.\n- **Low, drand is a single dependency with no recovery.** Accepted design. The beacon's key, genesis and period match the live evmnet info, and today's live round signature verifies on the contract.\n- **Low, GAMMA sent directly to the game is stranded.**\n- **Low, hook binds to the first IMD pool.** Front-runnable unless the hook launch creates hook and pool atomically, which the adaptation notes say it does. The hook is outside this manifest.\n- **Info, scope.** Lists what was read, what was not reached (the live PoolManager and IMD contracts on Robinhood Chain), and the checks run.\n\n**Checks run.** The build is clean. All 99 unit tests and both invariant suites pass. The manifest matches the brief's addresses, uses `$contract:DrandBeacon` and `$owner`, and stays within the schema.\n\n**For the adapter.** The medium needs either per-owner unclaimed accounting so a claimant never earns from their own fee, or a change to the stated guarantee. Everything else is advisory.","treeHash":null,"usage":{"cachedInputTokens":1410538,"inputTokens":482,"model":"claude-fable-5-1","outputTokens":50944,"runtime":"claude","turns":31,"wallClockMs":893569}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"bc39660800b5f882","findings":[{"citation":"resolved","description":"Not a defect: this is the stated rule 'failures split the evening Harvest'. Recorded so the author sees the concrete magnitude. The day pot receives the full tranche of every empty epoch plus 10% of every played epoch, and is split per fail with no weighting by when the energy was bought. The pot and the fails so far are public all day, so the last epoch of a day is the information-dominant moment to buy on Air (90% fail). On a day with no other players one 1,000-GAMMA energy (about 1,013 IMD at the fixture's 1:1 pool) takes the entire day-0 emission of 3,277,855 GAMMA; after the 10% matured fee the buyer receives 2,950,070 GAMMA. During the 7-day guard the competition for this is capped at 2,000 Air energies per epoch. Early-day buyers carry the same per-fail price with less information. If the author wants to blunt it, weighting fails by epochs remaining or capping a batch's share of the pot are rule changes outside this review's remit.","line":454,"path":"src/GammaGame.sol","reproduction":"Fixture (test/utils/GammaFixture.sol): warp to epochEnd(143)-30 (epoch 143 of day 0, nobody has played). alice: buyEnergies(3, 1, 2000e18, []) costs 1,012.67 IMD. Warp past dayEnd(0), set the drand rounds, settle(256). getDay(0): pot = 3,277,855,791,179,626,576,519,935, fails = 1, perFail = pot. Batch survivors = 0. After 24h claimMatured([id]) pays 2,950,070,212,061,663,918,867,942 GAMMA. Expected under the stated rules: exactly this; reported for its economic weight only.","severity":"info","snippet":"            uint256 perFail = pot / day.fails;\n            day.perFail = perFail;\n            winnings += perFail * day.fails;","title":"Quiet-day Harvest: a single last-epoch Air energy captures the whole day's emission (design property, numbers for the author)"},{"citation":"resolved","description":"Not a defect: the fee is designed to flow back to miners. The claim fee is split burn / unclaimed-bonus / Harvest. The self-bonus index (change 12) stops a claimant collecting the bonus third on their own unclaimed batches, but when nobody else holds unclaimed winnings the bonus third is routed to today's Harvest, and the Harvest third always is. Today's Harvest is split per fail among whoever plays today, so a miner who is the only (or dominant) player that day receives both thirds back the next day; only the burn third (26.7% of winnings at the 80% start fee) is a true cost. The 80%-to-10% schedule therefore describes a cost for a claimant who does not mine today, not for the dominant miner. Nobody else loses value; the thirds still reach 'the miners' as stated.","line":504,"path":"src/GammaGame.sol","reproduction":"Fixture: alice buys 100 Air energies on day 0; next day settle. alice buys 100 Air energies on day 1 (so today's fails are hers), then claim([day-0 batch]) at the 80% fee: winnings 3,273,748 GAMMA, paid 655,571, fee 2,618,998 of which burn 872,999 and 1,745,999 (bonus third + Harvest third) goes to getDay(1).pot (observed 1,748,189 including the epoch's 10%). Next day she claims day 1 matured: 4,506,449 GAMMA, which includes that 1,745,999 less the 10% fee. Expected by the stated rules: the same; reported so the fee schedule's real incidence is explicit.","severity":"info","snippet":"        if (totalUnclaimed == 0) {\n            harvestPart += bonusPart;\n        } else {","title":"A lone or dominant miner recovers up to two thirds of their own claim fee through the Harvest (design property, numbers for the author)"}],"hash":"114744ad6e330f1236a900d674e3efb1871cec5e410d6929e317014728636766","nodeId":"55cecb4b-a33d-4fe0-9e65-0db51c9858c5","outcome":"completed","summary":"The review is complete and `.imd-findings.json` holds the result.\n\n**Outcome: no reproducible defect in the economics.** I checked the GAMMA game against the Economic Security, Invariant and Flow Gap guides and could not break a conservation law, a value flow, or a stated guarantee. The file carries two informational notes with concrete numbers and a coverage row for every one of the 17 entry points plus four invariants, all `holds`.\n\nWhat I verified, with concrete probes where reasoning alone was not enough:\n\n- **Emission schedule.** `GammaEmission.released` is monotone across the year-1 boundary and saturates at exactly 800M, so the Reserve backs every tranche and no epoch's tranche is lost or doubled.\n- **Custody mirror.** Every inflow and outflow moves `gammaHeld` and the token balance together. Purchases are net zero within one call, and the hook fee is credited only when the tokens are already present.\n- **Claim fee split.** Payout plus burn leave the contract, the bonus and Harvest thirds stay inside. The self-bonus index cannot underflow and the index cannot overflow at any reachable magnitude.\n- **Settlement.** The cursor never passes the current epoch, empty runs stop at day boundaries, pots only receive funds for days not yet harvested, and the harvest's `totalUnclaimed` addition equals the sum of individual batch winnings.\n- **Randomness.** The beacon enforces a 64-byte signature and the BN254 pairing precompile rejects non-reduced coordinates, so each drand round yields exactly one randomness and a prover has no choice.\n- **Hook fee math.** Fee lands on the GAMMA side for every allowed swap shape and the game's own swaps are exempt. The hook and token are not in this manifest.\n\nTwo informational notes, both design properties rather than defects:\n\n1. On a quiet day, one Air energy bought in the last epoch takes the whole day's emission. One 1,000-GAMMA energy captured 3,277,855 GAMMA in the fixture.\n2. A lone or dominant miner receives the bonus and Harvest thirds of their own claim fee back the next day, so only the burn third is a true cost for them.\n\nBoth are already the stated rules, so neither should open a revision. Leads I closed as non-issues include the Slither divide-before-multiply and strict-equality lines, the reentrancy warnings on `buyEnergies`, and the known advisories on the fee decaying from the day's end and the sequencer-clock window, which the author has recorded as accepted trust assumptions.\n\nOne environment note for the verifier: the full test suite does not compile under this machine's 1.5 GB memory limit, so I ran scratch tests with sparse `--match-path` builds. The source itself builds cleanly. The scratch files are removed and no repository file was changed.","treeHash":null,"usage":{"cachedInputTokens":2101564,"inputTokens":706,"model":"claude-fable-5-1","outputTokens":44146,"runtime":"claude","turns":23,"wallClockMs":1851849}},{"artifacts":[],"attempt":1,"bundleHash":"9a2a81e1b3ccffcd3df4964b71c98b88e2717da62f35a9fb3fc3313ffefa38ea","device":"1a7ecd03bd365366","findings":[],"hash":"264f4dc5161ff819a3f6cfb1848a5bca3d1823e4e7a79b3f34691d8ed5b86300","nodeId":"277fb9e9-c1b2-4108-8135-174a4840fe1b","outcome":"completed","summary":"Work is complete and verified. Recap:\n\n**What reproduced and was fixed** (both in `src/GammaGame.sol`, tests in `test/GammaClaimBonus.t.sol`):\n\n- **Split-claim bonus (medium, 177ef269).** The auditor's proof failed on the original code with exactly the numbers in the finding. The fix adds a per-owner `selfBonusIndex` with checkpoints: a batch's bonus excludes the index growth the owner's own fees caused after that batch's Harvest, found by binary search over the owner's checkpoints. The withheld amount is passed on to the other unclaimed holders with that claim, or to the Harvest if there are none. A claim now pays the same in one call or many, and other holders get the same bonus either way. Six tests cover the auditor's scenario, a fuzz over every partition and machine, bonuses from others' fees, later-harvested batches, and the lone-claimant case.\n- **Stranded GAMMA (low, 84966529).** A plain transfer was unrecoverable. A permissionless `sweep()` now credits the unaccounted balance to today's Harvest through the same path as `donate`. Three tests.\n\n**Left unchanged, with reasons in ADAPTATION.md:** the 6-second beacon delay (requester's advisory value, the window only moves with a larger delay), the claim-fee decay from day end (requester's fee schedule, a rate is theirs), drand as the single source (their design), the hook binding race (hook is outside this manifest), and the scope note.\n\n**Checks:**\n\n| Check | Result |\n|---|---|\n| `forge build` | clean, GammaGame runtime 18,959 bytes |\n| `forge test` with the verifier's fuzz seed | 111 passed, 0 failed, both invariant suites included |\n| Auditor's proof on fixed code | passes |\n\nREADME and ADAPTATION.md are updated. No constructor, configuration, dependency or manifest changes were needed. Nothing was committed, per the rules.","treeHash":"e061ba05092e24c0d5a8b42b2922ad8fb312aa1f","usage":{"cachedInputTokens":1237664,"inputTokens":418,"model":"claude-fable-5-1","outputTokens":38354,"runtime":"claude","turns":39,"wallClockMs":1120780}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"2b9b0095482c54e6","findings":[{"citation":"resolved","description":"Fix 12 (ADAPTATION.md) is meant to stop a claimant collecting the bonus third of their own claim fee, \"however the claim is split\". The exclusion is stored per msg.sender (selfBonusIndex/_selfCheckpoints), but batch ownership is just the buying address, and buying from a second address is free. A player who buys batches from address A and address B, claims A first and B afterwards, gets B's share of A's bonus third: the same amount the original finding reported before the fix. The other holders lose that amount. Nothing stops a player from doing this, so the split-claim guarantee that fix 12 claims does not hold for any player who uses two addresses, and the claim fee's bonus third mostly goes back to the largest holder instead of to the other unclaimed holders.","line":510,"path":"src/GammaGame.sol","reproduction":"Use the project fixture (test/utils/GammaFixture.sol). On day 0: alice buys 500 Air energies (batch 0); address X buys 500 Air energies (batch 1); bob buys 10 Air energies (batch 2). Warp to dayEnd(0)+1, settle everything. Then alice claims [0], X claims [1], bob claims [2], all in the same block. With X = alice (one address split into two calls), alice's total is 649,073.29 GAMMA and bob gets 871,928.92. With X = carol (a second address held by the same player), the total for the two addresses is 1,072,838.17 GAMMA and bob gets 448,164.05. The draws are identical in both runs (same batch ids, same seed). Expected: claiming from two addresses pays the same as one address (649,073). Actual: it pays 423,764.88 GAMMA (65%) more, all taken from bob's bonus. The numbers were measured with a scratch test that runs both orders from one vm.snapshotState.","severity":"medium","snippet":"                uint256 self = selfBonusIndex[msg.sender] + step;\n                selfBonusIndex[msg.sender] = self;\n                _selfCheckpoints[msg.sender].push(SelfCheckpoint({index: bonusIndex, self: self}));","title":"Self-bonus exclusion (fix 12) is keyed by msg.sender, so a claimant using a second address recaptures the bonus third of their own fee"}],"hash":"555819c89dfa04d4032926ebc38c2d7b9fb5fdd3bb6abe9a3026417aa892aa47","nodeId":"ae209a4d-f87c-4663-910b-d6b7c4cbae45","outcome":"completed","summary":"I found one medium-severity defect. The review is in `.imd-findings.json`: the finding, a coverage row for all 17 entry points plus two invariant rows, and a note on scope. I changed no repository files; my scratch test is deleted.\n\n**Finding 1 (medium): fix 12 can be bypassed with a second address** (`src/GammaGame.sol:510`)\nFix 12 is meant to stop a claimant from collecting the bonus third of their own claim fee. It tracks this per `msg.sender`, but a batch belongs to whichever address bought it, and buying from a second address is free.\n- **Setup, all on day 0:** alice buys 500 Air energies, a second address buys 500 Air energies, and bob buys 10 Air energies. After the Harvest, they claim in that order in the same block.\n- **One address:** if alice holds both batches, she gets 649,073 GAMMA in total.\n- **Two addresses:** with the second batch on another address, the pair gets 1,072,838 GAMMA. That is exactly the amount the original finding reported before the fix.\n- **Who loses:** bob's bonus drops from 871,929 to 448,164 GAMMA.\n\nI measured these numbers with a scratch test that ran both versions from the same starting state, so the draws were identical. The finding carries no proof file: proofs are only required for critical and high findings, and the scratch test used the project's test fixture, which a proof file may not import. Whether this can be fixed at all is a question for the author, since the contract cannot tell that two addresses belong to one player.\n\n**Coverage**\n- **Holds:**\n  - **GammaGame:** `buyEnergies`, `settle`, `donate`, `sweep`, `onHookFee`, `initialize` and `unlockCallback`.\n  - **GammaHook:** `beforeSwap`, `afterSwap` (all four swap shapes) and `afterInitialize`.\n  - **DrandBeacon:** `prove`. The signature encoding is unique, so whoever proves a round first cannot pick its randomness.\n  - **GammaToken:** the four functions are plain solmate code with no custom logic.\n  - **Invariant:** the game's recorded GAMMA balance (`gammaHeld`) always equals what it actually holds.\n- **Finding 1:** `claim`, `claimMatured`, and the \"a split claim pays the same as one claim\" invariant.\n- **Also checked:** the emission schedule never decreases across the end of year 1 (I measured the values).\n- **Not done:** no fuzzing beyond the project's own tests, and no independent check of the BLS constants (the existing real-beacon test covers them).\n\nI left out the static-analysis leads I checked: the weak-randomness flag (the seed is drand), divide-before-multiply (remainders go to the Harvest), and the reentrancy flags (the game holds its lock, and only the pool manager can call back into it).","treeHash":null,"usage":{"cachedInputTokens":1259341,"inputTokens":32,"model":"claude-opus-5-5","outputTokens":25123,"runtime":"claude","turns":17,"wallClockMs":361296}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"970da38f1d564250","findings":[{"citation":"resolved","description":"Fix 12 (ADAPTATION.md) claims a claimant never collects the bonus third of their own fee however the claim is split. The exclusion is tracked only in selfBonusIndex[msg.sender]/_selfCheckpoints[msg.sender]; _bonusOf(owner,...) subtracts only that address's self index. Batches bought from a second address B (free to do) accrue the full bonusIndex step created by address A's claim fee, so the guarantee holds per address, not per player. Other unclaimed holders lose that amount. Merged from audit_flow. Severity low: a sybil limitation no per-address design can fully close; the documented guarantee should be narrowed, or the bonus third routed differently.","line":510,"path":"src/GammaGame.sol","reproduction":"Fixture: day 0 alice buys 500 Air (batch 0), address X buys 500 Air (batch 1), bob 10 Air (batch 2); warp past dayEnd(0), settle; claim [0] by alice, then [1] by X, then [2] by bob in one block. Trace: alice's claim raises bonusIndex by step and selfBonusIndex[alice] by step only. If X==alice, _bonusOf(alice) returns self=w1*step so batch 1 gets no bonus from alice's fee. If X==carol, selfBonusIndex[carol]==0 so batch 1 receives w1*step/INDEX_SCALE from alice's own fee; bob's bonus drops by the same amount (audit_flow measured 649,073 vs 1,072,838 GAMMA combined).","severity":"low","snippet":"                uint256 self = selfBonusIndex[msg.sender] + step;","title":"Self-bonus exclusion is per address: a claimant splitting batches across two addresses recaptures the bonus third of their own fee"}],"hash":"9a9a066a1a4df0d55b8cbfd82c0cbb14c02cee824a1f78afa6457664fb9b9ed4","nodeId":"bf183627-aa34-462c-ba0f-a7bb595fe6c1","outcome":"completed","summary":"I kept one finding, rated low, and wrote it to `.imd-findings.json` with a coverage row for all 17 entry points. I traced it in the code but did not run a test for it; its numbers come from the specialist's test.\n\n**The finding: splitting batches across two addresses gets around the self-bonus rule** (`src/GammaGame.sol:510`, merged from audit_flow, lowered from medium to low). Fix 12 in ADAPTATION.md says a claimant never collects the bonus share of their own claim fee, however they split the claim. But the code only records this per address, in `selfBonusIndex[msg.sender]`. So a player can buy with address A and a second address B, then claim A first. B's unclaimed batches then earn the bonus share of A's fee, and the other unclaimed holders lose that amount.\n- **Reproduction:** on day 0, alice buys 500 Air energies, a second address buys 500, and bob buys 10. After settling, they claim in that order.\n- **Specialist's measurement:** when the second address is alice herself, she gets 649,073 GAMMA. When it is a different address held by the same player, the two addresses get 1,072,838 together, and bob's share falls from 871,929 to 448,164.\n- **Why low:** no per-address design can link two wallets to one player. The real problem is that the stated guarantee is too broad: either narrow the wording in ADAPTATION.md or change where the bonus share goes.\n\n**Dropped:**\n- The `K_YEAR1` constant differs from what the contract computes by about 3,000 wei at most, and that dust stays in the Reserve. Too small to matter.\n- The two economics notes describe intended rules (\"failures split the Harvest\", \"fees flow back to miners\"), not defects.\n- The static-analysis leads didn't hold up: the strict equalities, the uninitialized local variables, and the return values the code ignores on purpose all behave as written.\n\n**What I checked and found sound:**\n- **Manifest:** `launch.json` is valid. It deploys `DrandBeacon` with no arguments, then `GammaGame` with the brief's pool manager, IMD address, `$contract:DrandBeacon` and `$owner`.\n- **Constructors:** both only store their arguments and make no external calls, so they will deploy on the empty test chain.\n- **Hook:** `GammaHook` is not in the manifest, so the factory doesn't need to mine an address for it. It has to be deployed and bound separately before `initialize` is called.\n\n**Coverage:**\n- `claim` and `claimMatured` are marked \"finding\" (finding 1).\n- The other 15 entry points are marked \"holds\". I traced the guards and accounting in each `GammaGame` and `GammaHook` function, but I did not open `GammaToken`. Its four rows (`approve`, `permit`, `transfer`, `transferFrom`) say \"holds\" on the assumption that they are the standard Solmate ERC20 functions, so they are really unverified.\n\nNo files in the repository were changed.","treeHash":null,"usage":{"cachedInputTokens":297664,"inputTokens":12,"model":"claude-opus-5-5","outputTokens":4331,"runtime":"claude","turns":6,"wallClockMs":50787}},{"artifacts":[],"attempt":1,"bundleHash":"82b9906df63325eecf8bffea02129b140904f4fc6e1d529e51c588a4271cb3c3","device":"e8b60b2d2d013a4d","findings":[],"hash":"bf291c2aa3a5d321315551c5f13364ecadb7c670b258b656f492c3976b85009d","nodeId":"be20f57a-15af-44e1-84d6-3c8e39d49642","outcome":"completed","summary":"Updated [launch.json](/home/imd2/.identitymd/work/a95d8284-6bc6-479d-ac62-7e115d14a150/be20f57a-15af-44e1-84d6-3c8e39d49642/launch.json), preserving the exact deployment order and constructor arguments and clarifying the approved behavior.\n\nManifest and ABI checks passed. `forge build` succeeded; `forge test` passed all 110 tests with the supplied seed. Only `launch.json` changed.","treeHash":"ab382bd639e0a59370c3b09b9d66819a5e2167e2","usage":{"cachedInputTokens":678144,"inputTokens":38394,"model":"gpt-6-astra","outputTokens":7830,"runtime":"codex","turns":10,"wallClockMs":513301}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"1b657df861f7b08b","findings":[],"hash":"cf2c44e2a565d5a4f723382e87513d8d5a567ca7752cb853ab63a2d356d01eba","nodeId":"d2392eaa-73cd-4e55-a95b-475220680c35","outcome":"completed","summary":"The review is complete. The findings file holds zero defects and a coverage row for all 17 entry points plus four invariant and scope rows. Only the findings file and the build output changed; no tracked file was touched.\n\n**Result: no access-control, trust-gap or asymmetry defect found.** Every state-changing entry point was traced against the three assigned Pashov guides.\n\n- **Permission model** has one role, the configurator, with one power: a single `initialize` call that must name the pool whose hook is bound to this game and must find the Reserve already funded. There is no owner, mint, withdraw, pause or upgrade. The compiled runtimes contain no DELEGATECALL, CALLCODE or SELFDESTRUCT and fit EIP-170.\n- **Inconsistent guards** were checked per storage variable. All writers of the game's accounted balance are backed by real token movement, so no caller can credit the Harvest with tokens that did not arrive. The hook-fee path, sweep and donate all enforce the same balance condition.\n- **Callbacks** hold. The pool manager only calls `unlockCallback` on the account that unlocked it, and only the game unlocks. The hook's game exemption keys on the swap sender, which only the game's own unlock can produce.\n- **Randomness** gives no actor a choice. The beacon accepts exactly 64 bytes, the pairing precompile rejects off-curve or out-of-field coordinates, and threshold BLS signatures are unique, so the stored seed is fixed per round.\n- **Trust assumption, not a defect**: a configurator who wires a malicious hook can stall purchases but cannot move GAMMA out, and the brief names the configurator as the launch owner.\n- **Open item outside this tree**: the separate hook launch must deliver exactly 800M GAMMA to the game or `initialize` reverts and the Reserve stays idle.\n\n**Checks that ran**: sources compiled; the failure-path suite and the hook suite ran one file at a time and all 21 tests passed. The sandbox's 1.5 GB memory cap kills the compiler on the full test tree, so the invariant suites could not be rerun here. That limit is recorded in the coverage record.","treeHash":null,"usage":{"cachedInputTokens":1556600,"inputTokens":452,"model":"claude-fable-5-1","outputTokens":33303,"runtime":"claude","turns":34,"wallClockMs":2585058}},{"artifacts":[],"attempt":1,"bundleHash":"f830ff0d9e2d870e72e7c6d846462acd6256df48c1b55e215fb1baeb272ec4c1","device":"b97527597dd934a0","findings":[],"hash":"da098f0b9c1095d1126a06ee77996ac0384f8127dd0c9e8271616e25813f1372","nodeId":"4134e9c5-cad2-4abf-a979-217bd870a76d","outcome":"completed","summary":"I added one test file, `test/GammaStateMachine.t.sol`, and found no defects in `GammaGame`, so there is no `.imd-findings.json`. With the verifier's seed, `forge test` passes all 125 tests across 19 suites: the 110 that were already there plus 15 new ones. The new suite also passes under a second seed. No source, config or `lib/` files were touched, nothing was installed, and the new file is not committed.\n\n**Invariant suite (`GammaStateMachineInvariantTest`).** Three players act in random order (48 runs × 64 calls, every revert counts as a failure). The actions are buying, moving time forward, settling with or without drand proofs, donating, sending GAMMA directly and then calling `sweep`, claiming, and two calls that must fail. After every call the handler checks:\n- **Nothing finished changes:** a drawn batch keeps its survivors, a claimed batch stays claimed, and a finalized epoch or Harvest keeps all its numbers.\n- **Counters only go forward:** `bonusIndex`, `totalBurned`, `cursor`, `activeCursor` and each player's `selfBonusIndex` never decrease, and a player's self index never exceeds the global one.\n- **Custody:** the game's GAMMA balance equals `gammaHeld` plus any GAMMA sent directly and not yet swept, and the game never keeps IMD.\n- **Claims pay what was shown:** once the game is fully settled, a claim of any subset of a player's batches pays exactly what `userBatches` predicts, fee and bonus included.\n- **Expected failures:** buying into an epoch whose drand round is already on-chain reverts with `EpochSeedKnown` and moves nothing; claiming someone else's batch reverts with `NotOwner`.\n\nFurther checks: the unreleased Reserve plus unclaimed winnings is always backed, and GAMMA supply stays fixed with the burn address holding `totalBurned`. At the end of each run, every player's matured winnings must pay out exactly and leave nothing unclaimed.\n\nThe claims-match-view check is not guaranteed to run in every random run, because some runs never reach a fully settled claim. I tried a guard requiring it and some runs tripped it, so I replaced the guard with the exact check in the end-of-run payout. That check only bites when a player still has something unclaimed at the end.\n\n**Edge-case tests (`GammaEdgeCasesTest`):**\n- A zero fee report from the hook is a no-op, and fee reports from the configurator or the PoolManager are rejected.\n- The hook cannot credit the same tokens twice.\n- `sweep` with nothing to sweep returns 0, and a direct transfer can only be swept once.\n- `settle(0)` does nothing.\n- A purchase with a zero IMD limit reverts and keeps no tokens.\n- Machine 4 and 255 are rejected, as are batch sizes 0, 501 and the max uint; a 500-energy batch is accepted.\n- A claimed batch collects no bonus from later claims.\n- `claimMatured` before the Harvest pays nothing.\n- Without drand, settlement waits even after 10 days and 100,000 blocks, while purchases keep working.\n- Fuzzed: a 0% fail rate keeps every energy and 100% keeps none, any machine number above 3 uses Air's 90% rate, and the claim fee stays within 80%–10%.\n\nThe one failure I hit during the work was a bug in my own test: `(who + 1) % 3` overflowed for the max uint. I fixed it; it was not a contract issue.","treeHash":"61c0838a497edf32230e23f3a0ca570732a25660","usage":{"cachedInputTokens":1024495,"inputTokens":28,"model":"claude-opus-5-5","outputTokens":29573,"runtime":"claude","turns":17,"wallClockMs":904803}}],"verification":[{"checks":[{"durationMs":112338,"exitCode":0,"name":"build","output":"Compiling 105 files with Solc 0.8.26\nSolc 0.8.26 finished in 112.25s\nCompiler run successful!\n","passed":true},{"durationMs":10604,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/GammaRealBeacon.t.sol:GammaFactoryDeploymentTest\n[PASS] test_factoryUsesExplicitConfiguratorAndEarlierBeacon() (gas: 368684)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 825.52µs (658.00µs CPU time)\n\nRan 1 test for test/GammaRevision.t.sol:GammaRevisionTest\n[PASS] test_buyIntoProvenEpochReverts() (gas: 166882)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.36ms (136.52µs CPU time)\n\nRan 1 test for test/GammaRevisionFailurePaths.t.sol:GammaRevisionFailurePathsTest\n[PASS] test_rejectedPurchaseIntoProvenEpochMovesNothingAndNextEpochOpens() (gas: 692521)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.33ms (558.39µs CPU time)\n\nRan 1 test for test/GammaRevision.t.sol:GammaRevisionFeeClockTest\n[PASS] test_feeHeldAtStartUntilGameGenesis() (gas: 213186)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.90ms (534.17µs CPU time)\n\nRan 10 tests for test/GammaLaunch.t.sol:GammaLaunchTest\n[PASS] test_backedGammaFeeIsCredited() (gas: 846713)\n[PASS] test_configuratorCannotDrainReserveThroughItsHook() (gas: 1033295)\n[PASS] test_heldGammaFollowsEveryFlow() (gas: 1700188)\n[PASS] test_hookFeesStillFlowWithTheRealHook() (gas: 372107)\n[PASS] test_imdFeeIsRejected() (gas: 766680)\n[PASS] test_initializeRecordsHeldGamma() (gas: 25870)\n[PASS] test_initializeRejectsHookBoundToAnotherGame() (gas: 378734)\n[PASS] test_initializeRejectsHookWithoutCode() (gas: 378453)\n[PASS] test_initializeRejectsKeyOfAnotherPool() (gas: 379035)\n[PASS] test_unbackedGammaFeeIsRejected() (gas: 765373)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 5.87ms (4.17ms CPU time)\n\nRan 10 tests for test/GammaHook.t.sol:GammaHookTest\n[PASS] test_buyExactInFeeInGamma() (gas: 373307)\n[PASS] test_exactOutBuyIsRejected() (gas: 163113)\n[PASS] test_feeSchedule() (gas: 125341)\n[PASS] test_gamePurchasesAreExempt() (gas: 631444)\n[PASS] test_onlyHookCanReportFees() (gas: 40264)\n[PASS] test_onlyPoolManager() (gas: 38749)\n[PASS] test_secondPoolRejected() (gas: 783463)\n[PASS] test_sellExactInFeeInGamma() (gas: 322964)\n[PASS] test_sniperFeeFundsFirstHarvest() (gas: 378788)\n[PASS] test_tradingFeesReachMiners() (gas: 1527630)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 5.93ms (3.04ms CPU time)\n\nRan 1 test for test/GammaLaunch.t.sol:GammaLaunchDeployTest\n[PASS] test_constructorsNeedNoOtherContract() (gas: 14308429)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 40.79ms (40.66ms CPU time)\n\nRan 2 tests for test/GammaRealBeacon.t.sol:GammaRealBeaconTest\n[PASS] test_invalidRealProofIsIgnoredWithoutDrawingBatch() (gas: 627365)\n[PASS] test_realProofUnblocksSettlementAndMaturedClaim() (gas: 1087693)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 67.54ms (11.24ms CPU time)\n\nRan 4 tests for test/DrandBeacon.t.sol:DrandBeaconTest\n[PASS] test_gas() (gas: 275605)\nLogs:\n  prove gas: 273345\n\n[PASS] test_idempotent() (gas: 303436)\n[PASS] test_rejectsWrongRound() (gas: 252494)\n[PASS] test_verifiesRealRounds() (gas: 558260)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 122.70ms (35.08ms CPU time)\n\nRan 11 tests for test/GammaFailurePaths.t.sol:GammaFailurePathsTest\n[PASS] test_claimArrayWithForeignBatchRevertsEveryEarlierClaim() (gas: 1458369)\n[PASS] test_duplicateBatchIdsCannotMultiplyPayoutOrBonus() (gas: 1842313)\n[PASS] test_failedDonationDoesNotFundHarvest() (gas: 107425)\n[PASS] test_failedSlippageRollsBackAutoSettlementAndTransfer() (gas: 1535012)\n[PASS] test_initializeRejectsMissingImdOrHookBeforeFunding() (gas: 77435)\n[PASS] test_initializeRollbackAllowsCorrectKeyAfterMistake() (gas: 875080)\n[PASS] test_missingAllowanceAndBalanceCannotCreateFreeEnergies() (gas: 328932)\n[PASS] test_outOfRangeClaimRevertsAndEmptyClaimsAreNoOps() (gas: 1152174)\n[PASS] test_uninitializedEntryPointsRejectWithoutMovingTokens() (gas: 188682)\n[PASS] test_unlockCallbackCannotBeCalledByPlayerOrConfigurator() (gas: 74366)\n[PASS] test_zeroDonationIsNoOp() (gas: 107772)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 143.81ms (5.12ms CPU time)\n\nRan 7 tests for test/DrandBeaconAdversarial.t.sol:DrandBeaconAdversarialTest\n[PASS] testFuzz_rejectsEveryNoncanonicalLength(uint8,uint64) (runs: 1000, μ: 37487, ~: 37384)\n[PASS] test_cannotAppendBytesToBiasRandomness() (gas: 309823)\n[PASS] test_rejectedProofDoesNotPreventLaterValidSubmission() (gas: 535229)\n[PASS] test_rejectsCoordinatesOutsideBaseField() (gas: 2004358)\n[PASS] test_rejectsInfinity() (gas: 256763)\n[PASS] test_rejectsOffCurveCoordinates() (gas: 2004162)\n[PASS] test_roundIsolationAndCacheCannotBeOverwritten() (gas: 573430)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 152.60ms (189.02ms CPU time)\n\nRan 28 tests for test/GammaGame.t.sol:GammaGameTest\n[PASS] testFuzz_dealIsPermutation(bytes32) (runs: 512, μ: 11557, ~: 11557)\n[PASS] testFuzz_survivorsBounded(bytes32,uint256,uint16,uint8) (runs: 512, μ: 63019, ~: 49460)\n[PASS] test_beaconProofsAttachedToPurchase() (gas: 1069106)\n[PASS] test_buyBounds() (gas: 108798)\n[PASS] test_buyBurnsGammaAndRecordsBatch() (gas: 519464)\n[PASS] test_buyRefundsUnusedImd() (gas: 466123)\n[PASS] test_buySlippage() (gas: 210988)\n[PASS] test_claimBeforeHarvestPaysNothing() (gas: 498398)\n[PASS] test_claimFeeSchedule() (gas: 82819)\n[PASS] test_claimMaturedSkipsYoungDays() (gas: 1226754)\n[PASS] test_claimNotOwnerReverts() (gas: 1044358)\n[PASS] test_claimPaysWinningsMinusFee() (gas: 1393192)\n[PASS] test_conservation() (gas: 4336099)\n[PASS] test_dealIsFair() (gas: 35263063)\n[PASS] test_earlyClaimersPayPatientOnes() (gas: 1997241)\n[PASS] test_emptyDayRollsIntoNextHarvest() (gas: 303257)\n[PASS] test_epochWaitsForBeacon() (gas: 913326)\n[PASS] test_gasNormalPurchase() (gas: 722696)\nLogs:\n  purchase gas (same epoch): 290794\n\n[PASS] test_gasPurchaseDoingMaxSettlement() (gas: 3978682)\nLogs:\n  purchase gas (8 large batches settled): 1687529\n\n[PASS] test_guardCapsFirstWeek() (gas: 1493984)\n[PASS] test_guardLiftsAfterAWeek() (gas: 2016558)\n[PASS] test_initializeNeedsReserve() (gas: 161034)\n[PASS] test_initializeOnlyConfigurator() (gas: 44139)\n[PASS] test_initializeOnlyOnce() (gas: 40968)\n[PASS] test_noFallbackSeedOnlyDrand() (gas: 2819062)\n[PASS] test_roundForEpochIsAfterClose() (gas: 49061)\n[PASS] test_survivalRatesMatchMachines() (gas: 24427121)\n[PASS] test_survivorsSplitShares_failsSplitHarvest() (gas: 1437240)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 158.55ms (400.18ms CPU time)\n\nRan 9 tests for test/GammaClaimBonus.t.sol:GammaClaimBonusTest\n[PASS] testFuzz_anySplitPaysTheSame(uint8,uint8) (runs: 512, μ: 3185038, ~: 3146138)\n[PASS] test_batchesHarvestedLaterAreNotPenalised() (gas: 3592096)\n[PASS] test_bonusFromOthersFeesIsStillPaid() (gas: 1980208)\n[PASS] test_loneClaimantSplitSendsTheThirdToTheHarvest() (gas: 1868807)\n[PASS] test_selfIndexCheckpointsOnlyOnFeeSharing() (gas: 1133688)\n[PASS] test_splitClaimPaysTheSameAsOneClaim() (gas: 2746761)\n[PASS] test_sweepCreditsStrayGammaToTheHarvest() (gas: 239265)\n[PASS] test_sweepNeedsInitialize() (gas: 44217)\n[PASS] test_sweptGammaIsPaidOutThroughTheHarvest() (gas: 1454374)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 158.62ms (163.99ms CPU time)\n\nRan 9 tests for test/GammaEmission.t.sol:GammaEmissionTest\n[PASS] testFuzz_betweenAdds(uint32,uint32,uint32) (runs: 512, μ: 31354, ~: 30693)\n[PASS] testFuzz_monotonic(uint32,uint32) (runs: 512, μ: 14438, ~: 13851)\n[PASS] test_day0Rate() (gas: 16896)\n[PASS] test_halvesEvery120Days() (gas: 17776)\n[PASS] test_neverExceedsReserve() (gas: 30834)\n[PASS] test_noJumpIntoTail() (gas: 19284)\n[PASS] test_startsAtZero() (gas: 5669)\n[PASS] test_tailHalvesEvery522Days() (gas: 19015)\n[PASS] test_year1IsExactly500M() (gas: 12312)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 186.34ms (328.57ms CPU time)\n\nRan 13 tests for test/GammaSettlementProperties.t.sol:GammaSettlementPropertiesTest\n[PASS] testFuzz_claimFeeIsBoundedAndNeverIncreases(uint32,uint32) (runs: 1000, μ: 37550, ~: 37599)\n[PASS] testFuzz_drawRiskIsMonotonicAndExtraEnergyAddsAtMostOneSurvivor(bytes32,uint256,uint16,uint16) (runs: 512, μ: 310400, ~: 212767)\n[PASS] testFuzz_eachEpochAccountsForItsEntireTranche(uint16[4],bytes32) (runs: 256, μ: 2033664, ~: 2030681)\n[PASS] testFuzz_roundIsFirstAtOrAfterRequiredDelay(uint32) (runs: 1000, μ: 28678, ~: 28622)\n[PASS] testFuzz_settlementCadenceCannotChangeOutcome(uint16[4],bytes32) (runs: 128, μ: 3371114, ~: 3379822)\n[PASS] test_badProofDoesNotBlockPurchaseOrInventRandomness() (gas: 1315919)\n[PASS] test_batchPaginationHandlesEmptyPagesAndKeepsNewestFirst() (gas: 1020375)\n[PASS] test_futureHarvestDoesNotReceivePastClaimBonuses() (gas: 2629624)\n[PASS] test_guardResetsPerEpochAndLiftsAtExactBoundary() (gas: 3018027)\n[PASS] test_harvestAtExactDayBoundaryUsesItsOwnUnit() (gas: 1272049)\n[PASS] test_maturedClaimSkipsUntilTheExact24HourBoundary() (gas: 1352420)\n[PASS] test_oneUnitDrawsOneBatchAndFinalizationNeedsAnotherUnit() (gas: 1785380)\n[PASS] test_settlementWorkIsClampedTo256EvenForMaximumInput() (gas: 68746975)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 186.23ms (697.13ms CPU time)\n\nRan 1 test for test/GammaInvariant.t.sol:GammaInvariantTest\n[PASS]\nGammaInvariantTest invariants:\n[PASS] invariant_cursorNeverAhead\n[PASS] invariant_gammaHeldMatchesBalance\n[PASS] invariant_solvent\n[PASS] invariant_supplyNeverGrows\n GammaInvariantTest invariants (runs: 64, calls: 4096, reverts: 0)\n\n╭--------------+----------+-------+---------+----------╮\n| Contract     | Selector | Calls | Reverts | Discards |\n+======================================================+\n| GammaHandler | buy      | 849   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| GammaHandler | claim    | 828   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| GammaHandler | proveAll | 787   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| GammaHandler | trade    | 810   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| GammaHandler | warp     | 822   | 0       | 0        |\n╰--------------+----------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.06s (1.06s CPU time)\n\nRan 1 test for test/GammaAccountingInvariant.t.sol:GammaAccountingInvariantTest\n[PASS]\nGammaAccountingInvariantTest invariants:\n[PASS] invariant_cashFlowsConserveGamma\n[PASS] invariant_everyLiabilityIsBacked\n[PASS] invariant_supplyAndBatchAggregates\n GammaAccountingInvariantTest invariants (runs: 128, calls: 12288, reverts: 0)\n\n╭------------------------+--------------------+-------+---------+----------╮\n| Contract               | Selector           | Calls | Reverts | Discards |\n+==========================================================================+\n| GammaAccountingHandler | advance            | 1797  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | buy                | 1738  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | claim              | 1774  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | donate             | 1796  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | rejectForeignClaim | 1735  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | settle             | 1719  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | trade              | 1729  | 0       | 0        |\n╰------------------------+--------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 10.51s (10.50s CPU time)\n\nRan 17 test suites in 10.51s (12.81s CPU time): 110 tests passed, 0 failed, 0 skipped (110 total tests)\n","passed":true},{"durationMs":75,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"DrandBeacon.prove(uint64,bytes)\",\"GammaGame.buyEnergies(uint8,uint256,uint256,(uint64,bytes)[])\",\"GammaGame.claim(uint256[])\",\"GammaGame.claimMatured(uint256[])\",\"GammaGame.donate(uint256)\",\"GammaGame.initialize((address,address,uint24,int24,address))\",\"GammaGame.onHookFee(address,uint256)\",\"GammaGame.settle(uint256,(uint64,bytes)[])\",\"GammaGame.sweep()\",\"GammaGame.unlockCallback(bytes)\",\"GammaHook.afterInitialize(address,(address,address,uint24,int24,address),uint160,int24)\",\"GammaHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"GammaHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"GammaToken.approve(address,uint256)\",\"GammaToken.permit(address,address,uint256,uint256,uint8,bytes32,bytes32)\",\"GammaToken.transfer(address,uint256)\",\"GammaToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\".gitmodules\":6,\"ADAPTATION.md\":295,\"PROJECT.md\":177,\"README.md\":100,\"foundry.toml\":32,\"frontend/.env.example\":10,\"frontend/.gitignore\":42,\"frontend/AGENTS.md\":9,\"frontend/eslint.config.mjs\":18,\"frontend/next.config.ts\":17,\"frontend/package.json\":32,\"frontend/pnpm-lock.yaml\":4535,\"frontend/pnpm-workspace.yaml\":3,\"frontend/scripts/gen-abi.mjs\":13,\"frontend/src/app/favicon.ico\":13,\"frontend/src/app/globals.css\":246,\"frontend/src/app/layout.tsx\":29,\"frontend/src/app/page.tsx\":63,\"frontend/src/components/Batches.tsx\":118,\"frontend/src/components/BuyPanel.tsx\":113,\"frontend/src/components/Economy.tsx\":249,\"frontend/src/components/Feed.tsx\":54,\"frontend/src/components/Footer.tsx\":37,\"frontend/src/components/Harvest.tsx\":53,\"frontend/src/components/Header.tsx\":92,\"frontend/src/components/Hero.tsx\":74,\"frontend/src/components/HowItWorks.tsx\":74,\"frontend/src/components/ImdSection.tsx\":72,\"frontend/src/components/Machines.tsx\":97,\"frontend/src/components/Providers.tsx\":45,\"frontend/src/components/Reactor.tsx\":120,\"frontend/src/components/VoxelCanvas.tsx\":159,\"frontend/src/hooks/useDemoGame.ts\":57,\"frontend/src/hooks/useLiveGame.ts\":343,\"frontend/src/lib/abi.ts\":4,\"frontend/src/lib/config.ts\":38,\"frontend/src/lib/drand.ts\":21,\"frontend/src/lib/format.ts\":26,\"frontend/src/lib/gamma.ts\":102,\"frontend/src/lib/sim.ts\":369,\"frontend/src/lib/types.ts\":70,\"frontend/src/lib/voxel.ts\":334,\"frontend/tsconfig.json\":34,\"launch.json\":19,\"launch/00-audit.json\":6,\"launch/01-game.json\":23,\"launch/02-hook.json\":28,\"launch/pay.sh\":45,\"script/DeployLocal.s.sol\":92,\"script/HookMiner.sol\":23,\"script/InitializeGame.s.sol\":36,\"src/DrandBeacon.sol\":58,\"src/GammaGame.sol\":741,\"src/GammaHook.sol\":204,\"src/GammaToken.sol\":15,\"src/interfaces/IGammaBeacon.sol\":17,\"src/interfaces/IGammaGame.sol\":12,\"src/interfaces/IGammaHook.sol\":13,\"src/libraries/GammaEmission.sol\":41,\"src/vendor/bls/BLS.sol\":634,\"src/vendor/bls/LICENSE\":21,\"src/vendor/bls/ModExp.sol\":643,\"src/vendor/bls/Precompiles.sol\":24,\"test/DrandBeacon.t.sol\":47,\"test/DrandBeaconAdversarial.t.sol\":76,\"test/GammaAccountingInvariant.t.sol\":327,\"test/GammaClaimBonus.t.sol\":237,\"test/GammaEmission.t.sol\":75,\"test/GammaFailurePaths.t.sol\":214,\"test/GammaGame.t.sol\":368,\"test/GammaHook.t.sol\":138,\"test/GammaInvariant.t.sol\":147,\"test/GammaLaunch.t.sol\":222,\"test/GammaRealBeacon.t.sol\":152,\"test/GammaRevision.t.sol\":42,\"test/GammaRevisionFailurePaths.t.sol\":32,\"test/GammaSettlementProperties.t.sol\":298,\"test/utils/GammaFixture.sol\":198},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":7006,"exitCode":0,"name":"slither","output":"[high/medium] weak-prng at src/GammaGame.sol:589: GammaGame.dealShares(bytes32) (src/GammaGame.sol#589-597) uses a weak PRNG: \"j = r % (i + 1) (src/GammaGame.sol#593)\"\n[medium/medium] divide-before-multiply at src/GammaGame.sol:412: GammaGame._finalizeEpoch(uint256,GammaGame.Epoch,bytes32) (src/GammaGame.sol#412-444) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/GammaGame.sol:446: GammaGame._harvest(uint256) (src/GammaGame.sol#446-463) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/GammaGame.sol:412: GammaGame._finalizeEpoch(uint256,GammaGame.Epoch,bytes32) (src/GammaGame.sol#412-444) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/GammaGame.sol:446: GammaGame._harvest(uint256) (src/GammaGame.sol#446-463) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/GammaGame.sol:412: GammaGame._finalizeEpoch(uint256,GammaGame.Epoch,bytes32) (src/GammaGame.sol#412-444) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/GammaHook.sol:75: GammaHook.feeBps() (src/GammaHook.sol#75-83) performs a multiplication on the result of a division:\n[medium/high] incorrect-equality at src/GammaGame.sol:359: GammaGame._settle(uint256) (src/GammaGame.sol#359-401) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/GammaGame.sol:637: GammaGame.epochSeed(uint256) (src/GammaGame.sol#637-640) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/GammaGame.sol:554: GammaGame._fund(uint256,uint256,uint8) (src/GammaGame.sol#554-558) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/GammaGame.sol:467: GammaGame._claim(uint256[],bool) (src/GammaGame.sol#467-525) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/GammaGame.sol:643: GammaGame.pendingRound() (src/GammaGame.sol#643-649) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/GammaGame.sol:601: GammaGame.currentEpoch() (src/GammaGame.sol#601-604) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/GammaHook.sol:123: GammaHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/GammaHook.sol#123-145) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/GammaGame.sol:643: GammaGame.pendingRound() (src/GammaGame.sol#643-649) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/GammaHook.sol:75: GammaHook.feeBps() (src/GammaHook.sol#75-83) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/GammaGame.sol:220: Reentrancy in GammaGame.buyEnergies(uint8,uint256,uint256,GammaGame.BeaconProof[]) (src/GammaGame.sol#220-268):\n[medium/medium] uninitialized-local at src/GammaGame.sol:580: GammaGame.drawSurvivors(bytes32,uint256,uint256,uint256).h (src/GammaGame.sol#580) is a local variable never initialized\n[medium/medium] uninitialized-local at src/GammaGame.sol:474: GammaGame._claim(uint256[],bool).selfTotal (src/GammaGame.sol#474) is a local variable never initialized\n[medium/medium] uninitialized-local at src/GammaGame.sol:471: GammaGame._claim(uint256[],bool).winningsTotal (src/GammaGame.sol#471) is a local variable never initialized\n[medium/medium] uninitialized-local at src/GammaGame.sol:420: GammaGame._finalizeEpoch(uint256,GammaGame.Epoch,bytes32).fails (src/GammaGame.sol#420) is a local variable never initialized\n[medium/medium] uninitialized-local at src/GammaGame.sol:473: GammaGame._claim(uint256[],bool).bonusTotal (src/GammaGame.sol#473) is a local variable never initialized\n[medium/medium] uninitialized-local at src/GammaGame.sol:418: GammaGame._finalizeEpoch(uint256,GammaGame.Epoch,bytes32).dealt (src/GammaGame.sol#418) is a local variable never initialized\n[medium/medium] uninitialized-local at src/GammaGame.sol:419: GammaGame._finalizeEpoch(uint256,GammaGame.Epoch,bytes32).paid (src/GammaGame.sol#419) is a local variable never initialized\n[medium/medium] uninitialized-local at src/GammaGame.sol:544: GammaGame._selfIndexAt(address,uint256).lo (src/GammaGame.sol#544) is a local variable never initialized\n[medium/medium] uninitialized-local at src/GammaGame.sol:472: GammaGame._claim(uint256[],bool).feeTotal (src/GammaGame.sol#472) is a local variable never initialized\n[medium/medium] unused-return at src/GammaGame.sol:319: GammaGame.unlockCallback(bytes) (src/GammaGame.sol#319-348) ignores return value by poolManager.settle() (src/GammaGame.sol#344)\n[medium/medium] unused-return at src/GammaGame.sol:352: GammaGame._prove(GammaGame.BeaconProof[]) (src/GammaGame.sol#352-357) ignores return value by beacon.prove(proofs[i].round,proofs[i].signature) (src/GammaGame.sol#355)\n[low/medium] events-maths at src/GammaGame.sol:308: GammaGame.onHookFee(Currency,uint256) (src/GammaGame.sol#308-314) should emit an event for: \n[low/medium] missing-zero-check at src/GammaHook.sol:43: GammaHook.constructor(IPoolManager,address,IGammaGame).imd_ (src/GammaHook.sol#43) lacks a zero-check on :\n[low/medium] missing-zero-check at src/GammaGame.sol:176: GammaGame.constructor(IPoolManager,ERC20,IGammaBeacon,address).configurator_ (src/GammaGame.sol#176) lacks a zero-check on :\n[low/medium] calls-loop at src/GammaGame.sol:628: GammaGame.roundForEpoch(uint256) (src/GammaGame.sol#628-634) has external calls inside a loop: g = beacon.genesis() (src/GammaGame.sol#630)\n[low/medium] calls-loop at src/GammaGame.sol:628: GammaGame.roundForEpoch(uint256) (src/GammaGame.sol#628-634) has external calls inside a loop: p = beacon.period() (src/GammaGame.sol#631)\n[low/medium] calls-loop at src/GammaGame.sol:628: GammaGame.roundForEpoch(uint256) (src/GammaGame.sol#628-634) has external calls inside a loop: g = beacon.genesis() (src/GammaGame.sol#630)\n[low/medium] calls-loop at src/GammaGame.sol:352: GammaGame._prove(GammaGame.BeaconProof[]) (src/GammaGame.sol#352-357) has external calls inside a loop: beacon.prove(proofs[i].round,proofs[i].signature) (src/GammaGame.sol#355)\n[low/medium] calls-loop at src/GammaGame.sol:352: GammaGame._prove(GammaGame.BeaconProof[]) (src/GammaGame.sol#352-357) has external calls inside a loop: beacon.prove(proofs[i].round,proofs[i].signature) (src/GammaGame.sol#355)\n[low/medium] calls-loop at src/GammaGame.sol:628: GammaGame.roundForEpoch(uint256) (src/GammaGame.sol#628-634) has external calls inside a loop: p = beacon.period() (src/GammaGame.sol#631)\n[low/medium] calls-loop at src/GammaGame.sol:628: GammaGame.roundForEpoch(uint256) (src/GammaGame.sol#628-634) has external calls inside a loop: g = beacon.genesis() (src/GammaGame.sol#630)\n[low/medium] calls-loop at src/GammaGame.sol:637: GammaGame.epochSeed(uint256) (src/GammaGame.sol#637-640) has external calls inside a loop: r = beacon.randomness(roundForEpoch(e)) (src/GammaGame.sol#638)\n[low/medium] calls-loop at src/GammaGame.sol:637: GammaGame.epochSeed(uint256) (src/GammaGame.sol#637-640) has external calls inside a loop: r = beacon.randomness(roundForEpoch(e)) (src/GammaGame.sol#638)\n[low/medium] calls-loop at src/GammaGame.sol:628: GammaGame.roundForEpoch(uint256) (src/GammaGame.sol#628-634) has external calls inside a loop: g = beacon.genesis() (src/GammaGame.sol#630)\n[low/medium] calls-loop at src/GammaGame.sol:637: GammaGame.epochSeed(uint256) (src/GammaGame.sol#637-640) has external calls inside a loop: r = beacon.randomness(roundForEpoch(e)) (src/GammaGame.sol#638)\n[low/medium] calls-loop at src/GammaGame.sol:628: GammaGame.roundForEpoch(uint256) (src/GammaGame.sol#628-634) has external calls inside a loop: p = beacon.period() (src/GammaGame.sol#631)\n[low/medium] calls-loop at src/GammaGame.sol:637: GammaGame.epochSeed(uint256) (src/GammaGame.sol#637-640) has external calls inside a loop: r = beacon.randomness(roundForEpoch(e)) (src/GammaGame.sol#638)\n[low/medium] calls-loop at src/GammaGame.sol:628: GammaGame.roundForEpoch(uint256) (src/GammaGame.sol#628-634) has external calls inside a loop: p = beacon.period() (src/GammaGame.sol#631)\n[low/medium] calls-loop at src/GammaGame.sol:352: GammaGame._prove(GammaGame.BeaconProof[]) (src/GammaGame.sol#352-357) has external calls inside a loop: beacon.randomness(proofs[i].round) != bytes32(0) (src/GammaGame.sol#354)\n[low/medium] calls-loop at src/GammaGame.sol:352: GammaGame._prove(GammaGame.BeaconProof[]) (src/GammaGame.sol#352-357) has external calls inside a loop: beacon.randomness(proofs[i].round) != bytes32(0) (src/GammaGame.sol#354)\n[low/medium] reentrancy-benign at src/GammaGame.sol:220: Reentrancy in GammaGame.buyEnergies(uint8,uint256,uint256,GammaGame.BeaconProof[]) (src/GammaGame.sol#220-268):\n[low/medium] reentrancy-benign at src/GammaGame.sol:220: Reentrancy in GammaGame.buyEnergies(uint8,uint256,uint256,GammaGame.BeaconProof[]) (src/GammaGame.sol#220-268):\n[low/medium] reentrancy-benign at src/GammaGame.sol:281: Reentrancy in GammaGame.settle(uint256,GammaGame.BeaconProof[]) (src/GammaGame.sol#281-285):\n[low/medium] reentrancy-events at src/GammaHook.sol:151: Reentrancy in GammaHook._collect(Currency,uint256) (src/GammaHook.sol#151-155):\n[low/medium] timestamp at src/GammaGame.sol:560: GammaGame._min(uint256,uint256) (src/GammaGame.sol#560-562) uses timestamp for comparisons\n[low/medium] timestamp at src/GammaGame.sol:220: GammaGame.buyEnergies(uint8,uint256,uint256,GammaGame.BeaconProof[]) (src/GammaGame.sol#220-268) uses timestamp for comparisons\n[low/medium] timestamp at src/GammaHook.sol:75: GammaHook.feeBps() (src/GammaHook.sol#75-83) uses timestamp for comparisons\n[low/medium] timestamp at src/GammaGame.sol:619: GammaGame.claimFeeBps(uint256) (src/GammaGame.sol#619-625) uses timestamp for comparisons\n[low/medium] timestamp at src/GammaGame.sol:297: GammaGame.sweep() (src/GammaGame.sol#297-302) uses timestamp for comparisons\n[low/medium] timestamp at src/GammaGame.sol:467: GammaGame._claim(uint256[],bool) (src/GammaGame.sol#467-525) uses timestamp for comparisons\n[low/medium] timestamp at src/GammaGame.sol:643: GammaGame.pendingRound() (src/GammaGame.sol#643-649) uses timestamp for comparisons\n[low/medium] timestamp at src/GammaGame.sol:637: GammaGame.epochSeed(uint256) (src/GammaGame.sol#637-640) uses timestamp for comparisons\n[low/medium] timestamp at src/GammaGame.sol:601: GammaGame.currentEpoch() (src/GammaGame.sol#601-604) uses timestamp for comparisons\n[low/medium] timestamp at src/GammaGame.sol:714: GammaGame.userBatches(address,uint256,uint256) (src/GammaGame.sol#714-740) uses timestamp for comparisons\n[low/medium] timestamp at src/GammaGame.sol:288: GammaGame.donate(uint256) (src/GammaGame.sol#288-293) uses timestamp for comparisons\n[low/medium] timestamp at src/GammaGame.sol:628: GammaGame.roundForEpoch(uint256) (src/GammaGame.sol#628-634) uses timestamp for comparisons\n[low/medium] timestamp at src/GammaHook.sol:103: GammaHook.beforeSwap(address,PoolKey,SwapParams,bytes) (src/GammaHook.sol#103-121) uses timestamp for comparisons\n[low/medium] timestamp at src/GammaGame.sol:575: GammaGame.drawSurvivors(bytes32,uint256,uint256,uint256) (src/GammaGame.sol#575-586) uses timestamp for comparisons\n[low/medium] timestamp at src/GammaGame.sol:359: GammaGame._settle(uint256) (src/GammaGame.sol#359-401) uses timestamp for comparisons\n[low/medium] timestamp at src/GammaGame.sol:281: GammaGame.settle(uint256,GammaGame.BeaconProof[]) (src/GammaGame.sol#281-285) uses timestamp for comparisons\n[low/medium] timestamp at src/GammaHook.sol:87: GammaHook.beforeInitialize(address,PoolKey,uint160) (src/GammaHook.sol#87-91) uses timestamp for comparisons\n[low/medium] timestamp at src/GammaGame.sol:186: GammaGame.initialize(PoolKey) (src/GammaGame.sol#186-213) uses timestamp for comparisons\n[low/medium] timestamp at src/GammaHook.sol:123: GammaHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/GammaHook.sol#123-145) uses timestamp for comparisons","passed":true},{"durationMs":566,"exitCode":0,"name":"aderyn","output":"[high] abi-encode-packed-hash-collision at src/vendor/bls/BLS.sol:479: `abi.encodePacked()` Hash Collision (2 places)\n[high] reentrancy-state-change at src/GammaGame.sol:200: Reentrancy: State change after external call (7 places)\n[high] unsafe-casting at src/GammaGame.sol:406: Unsafe Casting of integers (2 places)\n[low] costly-loop at src/GammaGame.sol:361: Costly operations inside loop (3 places)\n[low] division-before-multiplication at src/GammaHook.sol:80: Incorrect Order of Division and Multiplication\n[low] internal-function-used-once at src/vendor/bls/BLS.sol:423: Internal Function Used Only Once (4 places)\n[low] large-numeric-literal at src/GammaGame.sol:38: Large Numeric Literal (2 places)\n[low] literal-instead-of-constant at src/GammaGame.sol:226: Literal Instead of Constant (38 places)\n[low] local-variable-shadowing at src/GammaGame.sol:102: Local Variable Shadows State Variable\n[low] push-zero-opcode at src/DrandBeacon.sol:2: PUSH0 Opcode (11 places)\n[low] redundant-statement at src/GammaHook.sol:119: Redundant Statement\n[low] require-revert-in-loop at src/GammaGame.sol:475: Loop Contains `require`/`revert`\n[low] solmate-safe-transfer-lib at src/GammaGame.sol:5: Solmate's SafeTransferLib\n[low] state-change-without-event at src/GammaHook.sol:93: State Change Without Event\n[low] unchecked-return at src/GammaGame.sol:344: Unchecked Return\n[low] uninitialized-local-variable at src/GammaGame.sol:353: Uninitialized Local Variable (7 places)\n[low] unspecific-solidity-pragma at src/DrandBeacon.sol:2: Unspecific Solidity Pragma (8 places)\n[low] unused-import at src/GammaGame.sol:9: Unused Import (2 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"264f4dc5161ff819a3f6cfb1848a5bca3d1823e4e7a79b3f34691d8ed5b86300","verifiedTreeHash":"e061ba05092e24c0d5a8b42b2922ad8fb312aa1f","verifierVersion":"0.1.0+a6ee6548"},{"checks":[{"durationMs":119126,"exitCode":0,"name":"build","output":"Compiling 105 files with Solc 0.8.26\nSolc 0.8.26 finished in 119.01s\nCompiler run successful!\n","passed":true},{"durationMs":10670,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/GammaRealBeacon.t.sol:GammaFactoryDeploymentTest\n[PASS] test_factoryUsesExplicitConfiguratorAndEarlierBeacon() (gas: 368684)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 800.50µs (674.47µs CPU time)\n\nRan 1 test for test/GammaRevision.t.sol:GammaRevisionTest\n[PASS] test_buyIntoProvenEpochReverts() (gas: 166882)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.60ms (87.17µs CPU time)\n\nRan 1 test for test/GammaRevision.t.sol:GammaRevisionFeeClockTest\n[PASS] test_feeHeldAtStartUntilGameGenesis() (gas: 213186)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.78ms (268.17µs CPU time)\n\nRan 1 test for test/GammaRevisionFailurePaths.t.sol:GammaRevisionFailurePathsTest\n[PASS] test_rejectedPurchaseIntoProvenEpochMovesNothingAndNextEpochOpens() (gas: 692521)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 5.31ms (311.59µs CPU time)\n\nRan 10 tests for test/GammaHook.t.sol:GammaHookTest\n[PASS] test_buyExactInFeeInGamma() (gas: 373307)\n[PASS] test_exactOutBuyIsRejected() (gas: 163113)\n[PASS] test_feeSchedule() (gas: 125341)\n[PASS] test_gamePurchasesAreExempt() (gas: 631444)\n[PASS] test_onlyHookCanReportFees() (gas: 40264)\n[PASS] test_onlyPoolManager() (gas: 38749)\n[PASS] test_secondPoolRejected() (gas: 783463)\n[PASS] test_sellExactInFeeInGamma() (gas: 322964)\n[PASS] test_sniperFeeFundsFirstHarvest() (gas: 378788)\n[PASS] test_tradingFeesReachMiners() (gas: 1527630)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 6.94ms (2.12ms CPU time)\n\nRan 1 test for test/GammaLaunch.t.sol:GammaLaunchDeployTest\n[PASS] test_constructorsNeedNoOtherContract() (gas: 14308429)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 33.34ms (33.16ms CPU time)\n\nRan 10 tests for test/GammaLaunch.t.sol:GammaLaunchTest\n[PASS] test_backedGammaFeeIsCredited() (gas: 846713)\n[PASS] test_configuratorCannotDrainReserveThroughItsHook() (gas: 1033295)\n[PASS] test_heldGammaFollowsEveryFlow() (gas: 1700188)\n[PASS] test_hookFeesStillFlowWithTheRealHook() (gas: 372107)\n[PASS] test_imdFeeIsRejected() (gas: 766680)\n[PASS] test_initializeRecordsHeldGamma() (gas: 25870)\n[PASS] test_initializeRejectsHookBoundToAnotherGame() (gas: 378734)\n[PASS] test_initializeRejectsHookWithoutCode() (gas: 378453)\n[PASS] test_initializeRejectsKeyOfAnotherPool() (gas: 379035)\n[PASS] test_unbackedGammaFeeIsRejected() (gas: 765373)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 60.99ms (4.76ms CPU time)\n\nRan 2 tests for test/GammaRealBeacon.t.sol:GammaRealBeaconTest\n[PASS] test_invalidRealProofIsIgnoredWithoutDrawingBatch() (gas: 627365)\n[PASS] test_realProofUnblocksSettlementAndMaturedClaim() (gas: 1087693)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 102.40ms (4.50ms CPU time)\n\nRan 11 tests for test/GammaFailurePaths.t.sol:GammaFailurePathsTest\n[PASS] test_claimArrayWithForeignBatchRevertsEveryEarlierClaim() (gas: 1458369)\n[PASS] test_duplicateBatchIdsCannotMultiplyPayoutOrBonus() (gas: 1842313)\n[PASS] test_failedDonationDoesNotFundHarvest() (gas: 107425)\n[PASS] test_failedSlippageRollsBackAutoSettlementAndTransfer() (gas: 1535012)\n[PASS] test_initializeRejectsMissingImdOrHookBeforeFunding() (gas: 77435)\n[PASS] test_initializeRollbackAllowsCorrectKeyAfterMistake() (gas: 875080)\n[PASS] test_missingAllowanceAndBalanceCannotCreateFreeEnergies() (gas: 328932)\n[PASS] test_outOfRangeClaimRevertsAndEmptyClaimsAreNoOps() (gas: 1152174)\n[PASS] test_uninitializedEntryPointsRejectWithoutMovingTokens() (gas: 188682)\n[PASS] test_unlockCallbackCannotBeCalledByPlayerOrConfigurator() (gas: 74366)\n[PASS] test_zeroDonationIsNoOp() (gas: 107772)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 125.75ms (4.64ms CPU time)\n\nRan 7 tests for test/DrandBeaconAdversarial.t.sol:DrandBeaconAdversarialTest\n[PASS] testFuzz_rejectsEveryNoncanonicalLength(uint8,uint64) (runs: 1000, μ: 37487, ~: 37384)\n[PASS] test_cannotAppendBytesToBiasRandomness() (gas: 309823)\n[PASS] test_rejectedProofDoesNotPreventLaterValidSubmission() (gas: 535229)\n[PASS] test_rejectsCoordinatesOutsideBaseField() (gas: 2004358)\n[PASS] test_rejectsInfinity() (gas: 256763)\n[PASS] test_rejectsOffCurveCoordinates() (gas: 2004162)\n[PASS] test_roundIsolationAndCacheCannotBeOverwritten() (gas: 573430)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 186.05ms (174.37ms CPU time)\n\nRan 28 tests for test/GammaGame.t.sol:GammaGameTest\n[PASS] testFuzz_dealIsPermutation(bytes32) (runs: 512, μ: 11557, ~: 11557)\n[PASS] testFuzz_survivorsBounded(bytes32,uint256,uint16,uint8) (runs: 512, μ: 63019, ~: 49460)\n[PASS] test_beaconProofsAttachedToPurchase() (gas: 1069106)\n[PASS] test_buyBounds() (gas: 108798)\n[PASS] test_buyBurnsGammaAndRecordsBatch() (gas: 519464)\n[PASS] test_buyRefundsUnusedImd() (gas: 466123)\n[PASS] test_buySlippage() (gas: 210988)\n[PASS] test_claimBeforeHarvestPaysNothing() (gas: 498398)\n[PASS] test_claimFeeSchedule() (gas: 82819)\n[PASS] test_claimMaturedSkipsYoungDays() (gas: 1226754)\n[PASS] test_claimNotOwnerReverts() (gas: 1044358)\n[PASS] test_claimPaysWinningsMinusFee() (gas: 1393192)\n[PASS] test_conservation() (gas: 4336099)\n[PASS] test_dealIsFair() (gas: 35263063)\n[PASS] test_earlyClaimersPayPatientOnes() (gas: 1997241)\n[PASS] test_emptyDayRollsIntoNextHarvest() (gas: 303257)\n[PASS] test_epochWaitsForBeacon() (gas: 913326)\n[PASS] test_gasNormalPurchase() (gas: 722696)\nLogs:\n  purchase gas (same epoch): 290794\n\n[PASS] test_gasPurchaseDoingMaxSettlement() (gas: 3978682)\nLogs:\n  purchase gas (8 large batches settled): 1687529\n\n[PASS] test_guardCapsFirstWeek() (gas: 1493984)\n[PASS] test_guardLiftsAfterAWeek() (gas: 2016558)\n[PASS] test_initializeNeedsReserve() (gas: 161034)\n[PASS] test_initializeOnlyConfigurator() (gas: 44139)\n[PASS] test_initializeOnlyOnce() (gas: 40968)\n[PASS] test_noFallbackSeedOnlyDrand() (gas: 2819062)\n[PASS] test_roundForEpochIsAfterClose() (gas: 49061)\n[PASS] test_survivalRatesMatchMachines() (gas: 24427121)\n[PASS] test_survivorsSplitShares_failsSplitHarvest() (gas: 1437240)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 186.13ms (371.80ms CPU time)\n\nRan 9 tests for test/GammaClaimBonus.t.sol:GammaClaimBonusTest\n[PASS] testFuzz_anySplitPaysTheSame(uint8,uint8) (runs: 512, μ: 3185038, ~: 3146138)\n[PASS] test_batchesHarvestedLaterAreNotPenalised() (gas: 3592096)\n[PASS] test_bonusFromOthersFeesIsStillPaid() (gas: 1980208)\n[PASS] test_loneClaimantSplitSendsTheThirdToTheHarvest() (gas: 1868807)\n[PASS] test_selfIndexCheckpointsOnlyOnFeeSharing() (gas: 1133688)\n[PASS] test_splitClaimPaysTheSameAsOneClaim() (gas: 2746761)\n[PASS] test_sweepCreditsStrayGammaToTheHarvest() (gas: 239265)\n[PASS] test_sweepNeedsInitialize() (gas: 44217)\n[PASS] test_sweptGammaIsPaidOutThroughTheHarvest() (gas: 1454374)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 186.18ms (198.60ms CPU time)\n\nRan 9 tests for test/GammaEmission.t.sol:GammaEmissionTest\n[PASS] testFuzz_betweenAdds(uint32,uint32,uint32) (runs: 512, μ: 31354, ~: 30693)\n[PASS] testFuzz_monotonic(uint32,uint32) (runs: 512, μ: 14438, ~: 13851)\n[PASS] test_day0Rate() (gas: 16896)\n[PASS] test_halvesEvery120Days() (gas: 17776)\n[PASS] test_neverExceedsReserve() (gas: 30834)\n[PASS] test_noJumpIntoTail() (gas: 19284)\n[PASS] test_startsAtZero() (gas: 5669)\n[PASS] test_tailHalvesEvery522Days() (gas: 19015)\n[PASS] test_year1IsExactly500M() (gas: 12312)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 223.74ms (359.61ms CPU time)\n\nRan 13 tests for test/GammaSettlementProperties.t.sol:GammaSettlementPropertiesTest\n[PASS] testFuzz_claimFeeIsBoundedAndNeverIncreases(uint32,uint32) (runs: 1000, μ: 37550, ~: 37599)\n[PASS] testFuzz_drawRiskIsMonotonicAndExtraEnergyAddsAtMostOneSurvivor(bytes32,uint256,uint16,uint16) (runs: 512, μ: 310400, ~: 212767)\n[PASS] testFuzz_eachEpochAccountsForItsEntireTranche(uint16[4],bytes32) (runs: 256, μ: 2033664, ~: 2030681)\n[PASS] testFuzz_roundIsFirstAtOrAfterRequiredDelay(uint32) (runs: 1000, μ: 28678, ~: 28622)\n[PASS] testFuzz_settlementCadenceCannotChangeOutcome(uint16[4],bytes32) (runs: 128, μ: 3371114, ~: 3379822)\n[PASS] test_badProofDoesNotBlockPurchaseOrInventRandomness() (gas: 1315919)\n[PASS] test_batchPaginationHandlesEmptyPagesAndKeepsNewestFirst() (gas: 1020375)\n[PASS] test_futureHarvestDoesNotReceivePastClaimBonuses() (gas: 2629624)\n[PASS] test_guardResetsPerEpochAndLiftsAtExactBoundary() (gas: 3018027)\n[PASS] test_harvestAtExactDayBoundaryUsesItsOwnUnit() (gas: 1272049)\n[PASS] test_maturedClaimSkipsUntilTheExact24HourBoundary() (gas: 1352420)\n[PASS] test_oneUnitDrawsOneBatchAndFinalizationNeedsAnotherUnit() (gas: 1785380)\n[PASS] test_settlementWorkIsClampedTo256EvenForMaximumInput() (gas: 68746975)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 223.68ms (656.97ms CPU time)\n\nRan 4 tests for test/DrandBeacon.t.sol:DrandBeaconTest\n[PASS] test_gas() (gas: 275605)\nLogs:\n  prove gas: 273345\n\n[PASS] test_idempotent() (gas: 303436)\n[PASS] test_rejectsWrongRound() (gas: 252494)\n[PASS] test_verifiesRealRounds() (gas: 558260)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 223.83ms (12.21ms CPU time)\n\nRan 1 test for test/GammaInvariant.t.sol:GammaInvariantTest\n[PASS]\nGammaInvariantTest invariants:\n[PASS] invariant_cursorNeverAhead\n[PASS] invariant_gammaHeldMatchesBalance\n[PASS] invariant_solvent\n[PASS] invariant_supplyNeverGrows\n GammaInvariantTest invariants (runs: 64, calls: 4096, reverts: 0)\n\n╭--------------+----------+-------+---------+----------╮\n| Contract     | Selector | Calls | Reverts | Discards |\n+======================================================+\n| GammaHandler | buy      | 849   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| GammaHandler | claim    | 828   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| GammaHandler | proveAll | 787   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| GammaHandler | trade    | 810   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| GammaHandler | warp     | 822   | 0       | 0        |\n╰--------------+----------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.09s (1.09s CPU time)\n\nRan 1 test for test/GammaAccountingInvariant.t.sol:GammaAccountingInvariantTest\n[PASS]\nGammaAccountingInvariantTest invariants:\n[PASS] invariant_cashFlowsConserveGamma\n[PASS] invariant_everyLiabilityIsBacked\n[PASS] invariant_supplyAndBatchAggregates\n GammaAccountingInvariantTest invariants (runs: 128, calls: 12288, reverts: 0)\n\n╭------------------------+--------------------+-------+---------+----------╮\n| Contract               | Selector           | Calls | Reverts | Discards |\n+==========================================================================+\n| GammaAccountingHandler | advance            | 1797  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | buy                | 1738  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | claim              | 1774  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | donate             | 1796  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | rejectForeignClaim | 1735  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | settle             | 1719  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | trade              | 1729  | 0       | 0        |\n╰------------------------+--------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 10.56s (10.55s CPU time)\n\nRan 17 test suites in 10.57s (13.22s CPU time): 110 tests passed, 0 failed, 0 skipped (110 total tests)\n","passed":true},{"durationMs":82,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"DrandBeacon.prove(uint64,bytes)\",\"GammaGame.buyEnergies(uint8,uint256,uint256,(uint64,bytes)[])\",\"GammaGame.claim(uint256[])\",\"GammaGame.claimMatured(uint256[])\",\"GammaGame.donate(uint256)\",\"GammaGame.initialize((address,address,uint24,int24,address))\",\"GammaGame.onHookFee(address,uint256)\",\"GammaGame.settle(uint256,(uint64,bytes)[])\",\"GammaGame.sweep()\",\"GammaGame.unlockCallback(bytes)\",\"GammaHook.afterInitialize(address,(address,address,uint24,int24,address),uint160,int24)\",\"GammaHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"GammaHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"GammaToken.approve(address,uint256)\",\"GammaToken.permit(address,address,uint256,uint256,uint8,bytes32,bytes32)\",\"GammaToken.transfer(address,uint256)\",\"GammaToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\".gitmodules\":6,\"ADAPTATION.md\":295,\"PROJECT.md\":177,\"README.md\":100,\"foundry.toml\":32,\"frontend/.env.example\":10,\"frontend/.gitignore\":42,\"frontend/AGENTS.md\":9,\"frontend/eslint.config.mjs\":18,\"frontend/next.config.ts\":17,\"frontend/package.json\":32,\"frontend/pnpm-lock.yaml\":4535,\"frontend/pnpm-workspace.yaml\":3,\"frontend/scripts/gen-abi.mjs\":13,\"frontend/src/app/favicon.ico\":13,\"frontend/src/app/globals.css\":246,\"frontend/src/app/layout.tsx\":29,\"frontend/src/app/page.tsx\":63,\"frontend/src/components/Batches.tsx\":118,\"frontend/src/components/BuyPanel.tsx\":113,\"frontend/src/components/Economy.tsx\":249,\"frontend/src/components/Feed.tsx\":54,\"frontend/src/components/Footer.tsx\":37,\"frontend/src/components/Harvest.tsx\":53,\"frontend/src/components/Header.tsx\":92,\"frontend/src/components/Hero.tsx\":74,\"frontend/src/components/HowItWorks.tsx\":74,\"frontend/src/components/ImdSection.tsx\":72,\"frontend/src/components/Machines.tsx\":97,\"frontend/src/components/Providers.tsx\":45,\"frontend/src/components/Reactor.tsx\":120,\"frontend/src/components/VoxelCanvas.tsx\":159,\"frontend/src/hooks/useDemoGame.ts\":57,\"frontend/src/hooks/useLiveGame.ts\":343,\"frontend/src/lib/abi.ts\":4,\"frontend/src/lib/config.ts\":38,\"frontend/src/lib/drand.ts\":21,\"frontend/src/lib/format.ts\":26,\"frontend/src/lib/gamma.ts\":102,\"frontend/src/lib/sim.ts\":369,\"frontend/src/lib/types.ts\":70,\"frontend/src/lib/voxel.ts\":334,\"frontend/tsconfig.json\":34,\"launch.json\":19,\"launch/00-audit.json\":6,\"launch/01-game.json\":23,\"launch/02-hook.json\":28,\"launch/pay.sh\":45,\"script/DeployLocal.s.sol\":92,\"script/HookMiner.sol\":23,\"script/InitializeGame.s.sol\":36,\"src/DrandBeacon.sol\":58,\"src/GammaGame.sol\":741,\"src/GammaHook.sol\":204,\"src/GammaToken.sol\":15,\"src/interfaces/IGammaBeacon.sol\":17,\"src/interfaces/IGammaGame.sol\":12,\"src/interfaces/IGammaHook.sol\":13,\"src/libraries/GammaEmission.sol\":41,\"src/vendor/bls/BLS.sol\":634,\"src/vendor/bls/LICENSE\":21,\"src/vendor/bls/ModExp.sol\":643,\"src/vendor/bls/Precompiles.sol\":24,\"test/DrandBeacon.t.sol\":47,\"test/DrandBeaconAdversarial.t.sol\":76,\"test/GammaAccountingInvariant.t.sol\":327,\"test/GammaClaimBonus.t.sol\":237,\"test/GammaEmission.t.sol\":75,\"test/GammaFailurePaths.t.sol\":214,\"test/GammaGame.t.sol\":368,\"test/GammaHook.t.sol\":138,\"test/GammaInvariant.t.sol\":147,\"test/GammaLaunch.t.sol\":222,\"test/GammaRealBeacon.t.sol\":152,\"test/GammaRevision.t.sol\":42,\"test/GammaRevisionFailurePaths.t.sol\":32,\"test/GammaSettlementProperties.t.sol\":298,\"test/utils/GammaFixture.sol\":198},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"bf291c2aa3a5d321315551c5f13364ecadb7c670b258b656f492c3976b85009d","verifiedTreeHash":"ab382bd639e0a59370c3b09b9d66819a5e2167e2","verifierVersion":"0.1.0+a6ee6548"},{"checks":[{"durationMs":132442,"exitCode":0,"name":"build","output":"Compiling 106 files with Solc 0.8.26\nSolc 0.8.26 finished in 132.36s\nCompiler run successful!\n","passed":true},{"durationMs":10175,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/GammaRealBeacon.t.sol:GammaFactoryDeploymentTest\n[PASS] test_factoryUsesExplicitConfiguratorAndEarlierBeacon() (gas: 368684)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 769.71µs (620.38µs CPU time)\n\nRan 1 test for test/GammaRevision.t.sol:GammaRevisionTest\n[PASS] test_buyIntoProvenEpochReverts() (gas: 166882)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.47ms (84.67µs CPU time)\n\nRan 1 test for test/GammaRevision.t.sol:GammaRevisionFeeClockTest\n[PASS] test_feeHeldAtStartUntilGameGenesis() (gas: 213186)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.61ms (433.56µs CPU time)\n\nRan 1 test for test/GammaRevisionFailurePaths.t.sol:GammaRevisionFailurePathsTest\n[PASS] test_rejectedPurchaseIntoProvenEpochMovesNothingAndNextEpochOpens() (gas: 692521)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.77ms (333.24µs CPU time)\n\nRan 10 tests for test/GammaHook.t.sol:GammaHookTest\n[PASS] test_buyExactInFeeInGamma() (gas: 373307)\n[PASS] test_exactOutBuyIsRejected() (gas: 163113)\n[PASS] test_feeSchedule() (gas: 125341)\n[PASS] test_gamePurchasesAreExempt() (gas: 631444)\n[PASS] test_onlyHookCanReportFees() (gas: 40264)\n[PASS] test_onlyPoolManager() (gas: 38749)\n[PASS] test_secondPoolRejected() (gas: 783463)\n[PASS] test_sellExactInFeeInGamma() (gas: 322964)\n[PASS] test_sniperFeeFundsFirstHarvest() (gas: 378788)\n[PASS] test_tradingFeesReachMiners() (gas: 1527630)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 4.81ms (2.46ms CPU time)\n\nRan 1 test for test/GammaLaunch.t.sol:GammaLaunchDeployTest\n[PASS] test_constructorsNeedNoOtherContract() (gas: 14308429)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 17.58ms (17.45ms CPU time)\n\nRan 2 tests for test/GammaRealBeacon.t.sol:GammaRealBeaconTest\n[PASS] test_invalidRealProofIsIgnoredWithoutDrawingBatch() (gas: 627365)\n[PASS] test_realProofUnblocksSettlementAndMaturedClaim() (gas: 1087693)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 173.80ms (4.05ms CPU time)\n\nRan 7 tests for test/DrandBeaconAdversarial.t.sol:DrandBeaconAdversarialTest\n[PASS] testFuzz_rejectsEveryNoncanonicalLength(uint8,uint64) (runs: 1000, μ: 37490, ~: 37384)\n[PASS] test_cannotAppendBytesToBiasRandomness() (gas: 309823)\n[PASS] test_rejectedProofDoesNotPreventLaterValidSubmission() (gas: 535229)\n[PASS] test_rejectsCoordinatesOutsideBaseField() (gas: 2004358)\n[PASS] test_rejectsInfinity() (gas: 256763)\n[PASS] test_rejectsOffCurveCoordinates() (gas: 2004162)\n[PASS] test_roundIsolationAndCacheCannotBeOverwritten() (gas: 573430)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 188.21ms (173.12ms CPU time)\n\nRan 11 tests for test/GammaFailurePaths.t.sol:GammaFailurePathsTest\n[PASS] test_claimArrayWithForeignBatchRevertsEveryEarlierClaim() (gas: 1458369)\n[PASS] test_duplicateBatchIdsCannotMultiplyPayoutOrBonus() (gas: 1842313)\n[PASS] test_failedDonationDoesNotFundHarvest() (gas: 107425)\n[PASS] test_failedSlippageRollsBackAutoSettlementAndTransfer() (gas: 1535012)\n[PASS] test_initializeRejectsMissingImdOrHookBeforeFunding() (gas: 77435)\n[PASS] test_initializeRollbackAllowsCorrectKeyAfterMistake() (gas: 875080)\n[PASS] test_missingAllowanceAndBalanceCannotCreateFreeEnergies() (gas: 328932)\n[PASS] test_outOfRangeClaimRevertsAndEmptyClaimsAreNoOps() (gas: 1152174)\n[PASS] test_uninitializedEntryPointsRejectWithoutMovingTokens() (gas: 188682)\n[PASS] test_unlockCallbackCannotBeCalledByPlayerOrConfigurator() (gas: 74366)\n[PASS] test_zeroDonationIsNoOp() (gas: 107772)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 191.56ms (7.02ms CPU time)\n\nRan 10 tests for test/GammaLaunch.t.sol:GammaLaunchTest\n[PASS] test_backedGammaFeeIsCredited() (gas: 846713)\n[PASS] test_configuratorCannotDrainReserveThroughItsHook() (gas: 1033295)\n[PASS] test_heldGammaFollowsEveryFlow() (gas: 1700188)\n[PASS] test_hookFeesStillFlowWithTheRealHook() (gas: 372107)\n[PASS] test_imdFeeIsRejected() (gas: 766680)\n[PASS] test_initializeRecordsHeldGamma() (gas: 25870)\n[PASS] test_initializeRejectsHookBoundToAnotherGame() (gas: 378734)\n[PASS] test_initializeRejectsHookWithoutCode() (gas: 378453)\n[PASS] test_initializeRejectsKeyOfAnotherPool() (gas: 379035)\n[PASS] test_unbackedGammaFeeIsRejected() (gas: 765373)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 189.73ms (4.35ms CPU time)\n\nRan 28 tests for test/GammaGame.t.sol:GammaGameTest\n[PASS] testFuzz_dealIsPermutation(bytes32) (runs: 512, μ: 11557, ~: 11557)\n[PASS] testFuzz_survivorsBounded(bytes32,uint256,uint16,uint8) (runs: 512, μ: 62480, ~: 49202)\n[PASS] test_beaconProofsAttachedToPurchase() (gas: 1069106)\n[PASS] test_buyBounds() (gas: 108798)\n[PASS] test_buyBurnsGammaAndRecordsBatch() (gas: 519464)\n[PASS] test_buyRefundsUnusedImd() (gas: 466123)\n[PASS] test_buySlippage() (gas: 210988)\n[PASS] test_claimBeforeHarvestPaysNothing() (gas: 498398)\n[PASS] test_claimFeeSchedule() (gas: 82819)\n[PASS] test_claimMaturedSkipsYoungDays() (gas: 1226754)\n[PASS] test_claimNotOwnerReverts() (gas: 1044358)\n[PASS] test_claimPaysWinningsMinusFee() (gas: 1393192)\n[PASS] test_conservation() (gas: 4336099)\n[PASS] test_dealIsFair() (gas: 35263063)\n[PASS] test_earlyClaimersPayPatientOnes() (gas: 1997241)\n[PASS] test_emptyDayRollsIntoNextHarvest() (gas: 303257)\n[PASS] test_epochWaitsForBeacon() (gas: 913326)\n[PASS] test_gasNormalPurchase() (gas: 722696)\nLogs:\n  purchase gas (same epoch): 290794\n\n[PASS] test_gasPurchaseDoingMaxSettlement() (gas: 3978682)\nLogs:\n  purchase gas (8 large batches settled): 1687529\n\n[PASS] test_guardCapsFirstWeek() (gas: 1493984)\n[PASS] test_guardLiftsAfterAWeek() (gas: 2016558)\n[PASS] test_initializeNeedsReserve() (gas: 161034)\n[PASS] test_initializeOnlyConfigurator() (gas: 44139)\n[PASS] test_initializeOnlyOnce() (gas: 40968)\n[PASS] test_noFallbackSeedOnlyDrand() (gas: 2819062)\n[PASS] test_roundForEpochIsAfterClose() (gas: 49061)\n[PASS] test_survivalRatesMatchMachines() (gas: 24427121)\n[PASS] test_survivorsSplitShares_failsSplitHarvest() (gas: 1437240)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 191.63ms (361.82ms CPU time)\n\nRan 4 tests for test/DrandBeacon.t.sol:DrandBeaconTest\n[PASS] test_gas() (gas: 275605)\nLogs:\n  prove gas: 273345\n\n[PASS] test_idempotent() (gas: 303436)\n[PASS] test_rejectsWrongRound() (gas: 252494)\n[PASS] test_verifiesRealRounds() (gas: 558260)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 191.74ms (8.78ms CPU time)\n\nRan 9 tests for test/GammaClaimBonus.t.sol:GammaClaimBonusTest\n[PASS] testFuzz_anySplitPaysTheSame(uint8,uint8) (runs: 512, μ: 3184492, ~: 3146138)\n[PASS] test_batchesHarvestedLaterAreNotPenalised() (gas: 3592096)\n[PASS] test_bonusFromOthersFeesIsStillPaid() (gas: 1980208)\n[PASS] test_loneClaimantSplitSendsTheThirdToTheHarvest() (gas: 1868807)\n[PASS] test_selfIndexCheckpointsOnlyOnFeeSharing() (gas: 1133688)\n[PASS] test_splitClaimPaysTheSameAsOneClaim() (gas: 2746761)\n[PASS] test_sweepCreditsStrayGammaToTheHarvest() (gas: 239265)\n[PASS] test_sweepNeedsInitialize() (gas: 44217)\n[PASS] test_sweptGammaIsPaidOutThroughTheHarvest() (gas: 1454374)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 191.68ms (198.96ms CPU time)\n\nRan 13 tests for test/GammaSettlementProperties.t.sol:GammaSettlementPropertiesTest\n[PASS] testFuzz_claimFeeIsBoundedAndNeverIncreases(uint32,uint32) (runs: 1000, μ: 37542, ~: 37599)\n[PASS] testFuzz_drawRiskIsMonotonicAndExtraEnergyAddsAtMostOneSurvivor(bytes32,uint256,uint16,uint16) (runs: 512, μ: 312825, ~: 225296)\n[PASS] testFuzz_eachEpochAccountsForItsEntireTranche(uint16[4],bytes32) (runs: 256, μ: 2032020, ~: 2029512)\n[PASS] testFuzz_roundIsFirstAtOrAfterRequiredDelay(uint32) (runs: 1000, μ: 28677, ~: 28622)\n[PASS] testFuzz_settlementCadenceCannotChangeOutcome(uint16[4],bytes32) (runs: 128, μ: 3369272, ~: 3373110)\n[PASS] test_badProofDoesNotBlockPurchaseOrInventRandomness() (gas: 1315919)\n[PASS] test_batchPaginationHandlesEmptyPagesAndKeepsNewestFirst() (gas: 1020375)\n[PASS] test_futureHarvestDoesNotReceivePastClaimBonuses() (gas: 2629624)\n[PASS] test_guardResetsPerEpochAndLiftsAtExactBoundary() (gas: 3018027)\n[PASS] test_harvestAtExactDayBoundaryUsesItsOwnUnit() (gas: 1272049)\n[PASS] test_maturedClaimSkipsUntilTheExact24HourBoundary() (gas: 1352420)\n[PASS] test_oneUnitDrawsOneBatchAndFinalizationNeedsAnotherUnit() (gas: 1785380)\n[PASS] test_settlementWorkIsClampedTo256EvenForMaximumInput() (gas: 68746975)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 191.66ms (694.24ms CPU time)\n\nRan 14 tests for test/GammaStateMachine.t.sol:GammaEdgeCasesTest\n[PASS] testFuzz_claimFeeEndpoints(uint256,uint256) (runs: 1000, μ: 20480, ~: 20524)\n[PASS] testFuzz_failRateIsClampedToAir(uint256) (runs: 1000, μ: 6025, ~: 6022)\n[PASS] testFuzz_survivorEdgesOfFailRate(bytes32,uint256,uint256) (runs: 1000, μ: 159063, ~: 157435)\n[PASS] test_claimMaturedBeforeHarvestPaysNothing() (gas: 832090)\n[PASS] test_claimedBatchCollectsNoLaterBonus() (gas: 1988784)\n[PASS] test_feeReportFromConfiguratorOrPoolManagerIsRejected() (gas: 110903)\n[PASS] test_hookCannotCreditTheSameTokensTwice() (gas: 156801)\n[PASS] test_machineAndAmountEdgesAreRejected() (gas: 621659)\n[PASS] test_noSettlementWithoutBeaconEvenAfterManyDays() (gas: 910347)\n[PASS] test_settleWithZeroUnitsMovesNothing() (gas: 602951)\n[PASS] test_strayTransferSweptOnceOnly() (gas: 153240)\n[PASS] test_sweepWithNothingStrayReturnsZeroAndChangesNothing() (gas: 91933)\n[PASS] test_zeroHookFeeIsANoOp() (gas: 111306)\n[PASS] test_zeroImdLimitCannotBuyAndKeepsNoTokens() (gas: 213279)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 191.77ms (258.51ms CPU time)\n\nRan 9 tests for test/GammaEmission.t.sol:GammaEmissionTest\n[PASS] testFuzz_betweenAdds(uint32,uint32,uint32) (runs: 512, μ: 31350, ~: 30374)\n[PASS] testFuzz_monotonic(uint32,uint32) (runs: 512, μ: 14421, ~: 13855)\n[PASS] test_day0Rate() (gas: 16896)\n[PASS] test_halvesEvery120Days() (gas: 17776)\n[PASS] test_neverExceedsReserve() (gas: 30834)\n[PASS] test_noJumpIntoTail() (gas: 19284)\n[PASS] test_startsAtZero() (gas: 5669)\n[PASS] test_tailHalvesEvery522Days() (gas: 19015)\n[PASS] test_year1IsExactly500M() (gas: 12312)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 191.87ms (383.65ms CPU time)\n\nRan 1 test for test/GammaInvariant.t.sol:GammaInvariantTest\n[PASS]\nGammaInvariantTest invariants:\n[PASS] invariant_cursorNeverAhead\n[PASS] invariant_gammaHeldMatchesBalance\n[PASS] invariant_solvent\n[PASS] invariant_supplyNeverGrows\n GammaInvariantTest invariants (runs: 64, calls: 4096, reverts: 0)\n\n╭--------------+----------+-------+---------+----------╮\n| Contract     | Selector | Calls | Reverts | Discards |\n+======================================================+\n| GammaHandler | buy      | 849   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| GammaHandler | claim    | 828   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| GammaHandler | proveAll | 787   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| GammaHandler | trade    | 810   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| GammaHandler | warp     | 822   | 0       | 0        |\n╰--------------+----------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.02s (1.02s CPU time)\n\nRan 1 test for test/GammaStateMachine.t.sol:GammaStateMachineInvariantTest\n[PASS]\nGammaStateMachineInvariantTest invariants:\n[PASS] invariant_custodyMatchesMirrorPlusStray\n[PASS] invariant_reserveNeverBelowUnreleased\n[PASS] invariant_supplyFixed\n GammaStateMachineInvariantTest invariants (runs: 48, calls: 3072, reverts: 0)\n\n╭--------------------------+-------------------+-------+---------+----------╮\n| Contract                 | Selector          | Calls | Reverts | Discards |\n+===========================================================================+\n| GammaStateMachineHandler | advance           | 326   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | buy               | 337   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | buyIntoKnownEpoch | 334   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | claimExact        | 345   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | claimLagging      | 359   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | donate            | 319   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | foreignClaim      | 341   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | settle            | 356   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | sweep             | 355   | 0       | 0        |\n╰--------------------------+-------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.06s (1.06s CPU time)\n\nRan 1 test for test/GammaAccountingInvariant.t.sol:GammaAccountingInvariantTest\n[PASS]\nGammaAccountingInvariantTest invariants:\n[PASS] invariant_cashFlowsConserveGamma\n[PASS] invariant_everyLiabilityIsBacked\n[PASS] invariant_supplyAndBatchAggregates\n GammaAccountingInvariantTest invariants (runs: 128, calls: 12288, reverts: 0)\n\n╭------------------------+--------------------+-------+---------+----------╮\n| Contract               | Selector           | Calls | Reverts | Discards |\n+==========================================================================+\n| GammaAccountingHandler | advance            | 1797  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | buy                | 1738  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | claim              | 1774  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | donate             | 1796  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | rejectForeignClaim | 1735  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | settle             | 1719  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | trade              | 1729  | 0       | 0        |\n╰------------------------+--------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 10.08s (10.08s CPU time)\n\nRan 19 test suites in 10.08s (14.09s CPU time): 125 tests passed, 0 failed, 0 skipped (125 total tests)\n","passed":true},{"durationMs":79,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"DrandBeacon.prove(uint64,bytes)\",\"GammaGame.buyEnergies(uint8,uint256,uint256,(uint64,bytes)[])\",\"GammaGame.claim(uint256[])\",\"GammaGame.claimMatured(uint256[])\",\"GammaGame.donate(uint256)\",\"GammaGame.initialize((address,address,uint24,int24,address))\",\"GammaGame.onHookFee(address,uint256)\",\"GammaGame.settle(uint256,(uint64,bytes)[])\",\"GammaGame.sweep()\",\"GammaGame.unlockCallback(bytes)\",\"GammaHook.afterInitialize(address,(address,address,uint24,int24,address),uint160,int24)\",\"GammaHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"GammaHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"GammaToken.approve(address,uint256)\",\"GammaToken.permit(address,address,uint256,uint256,uint8,bytes32,bytes32)\",\"GammaToken.transfer(address,uint256)\",\"GammaToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\".gitmodules\":6,\"ADAPTATION.md\":295,\"PROJECT.md\":177,\"README.md\":100,\"foundry.toml\":32,\"frontend/.env.example\":10,\"frontend/.gitignore\":42,\"frontend/AGENTS.md\":9,\"frontend/eslint.config.mjs\":18,\"frontend/next.config.ts\":17,\"frontend/package.json\":32,\"frontend/pnpm-lock.yaml\":4535,\"frontend/pnpm-workspace.yaml\":3,\"frontend/scripts/gen-abi.mjs\":13,\"frontend/src/app/favicon.ico\":13,\"frontend/src/app/globals.css\":246,\"frontend/src/app/layout.tsx\":29,\"frontend/src/app/page.tsx\":63,\"frontend/src/components/Batches.tsx\":118,\"frontend/src/components/BuyPanel.tsx\":113,\"frontend/src/components/Economy.tsx\":249,\"frontend/src/components/Feed.tsx\":54,\"frontend/src/components/Footer.tsx\":37,\"frontend/src/components/Harvest.tsx\":53,\"frontend/src/components/Header.tsx\":92,\"frontend/src/components/Hero.tsx\":74,\"frontend/src/components/HowItWorks.tsx\":74,\"frontend/src/components/ImdSection.tsx\":72,\"frontend/src/components/Machines.tsx\":97,\"frontend/src/components/Providers.tsx\":45,\"frontend/src/components/Reactor.tsx\":120,\"frontend/src/components/VoxelCanvas.tsx\":159,\"frontend/src/hooks/useDemoGame.ts\":57,\"frontend/src/hooks/useLiveGame.ts\":343,\"frontend/src/lib/abi.ts\":4,\"frontend/src/lib/config.ts\":38,\"frontend/src/lib/drand.ts\":21,\"frontend/src/lib/format.ts\":26,\"frontend/src/lib/gamma.ts\":102,\"frontend/src/lib/sim.ts\":369,\"frontend/src/lib/types.ts\":70,\"frontend/src/lib/voxel.ts\":334,\"frontend/tsconfig.json\":34,\"launch.json\":19,\"launch/00-audit.json\":6,\"launch/01-game.json\":23,\"launch/02-hook.json\":28,\"launch/pay.sh\":45,\"script/DeployLocal.s.sol\":92,\"script/HookMiner.sol\":23,\"script/InitializeGame.s.sol\":36,\"src/DrandBeacon.sol\":58,\"src/GammaGame.sol\":741,\"src/GammaHook.sol\":204,\"src/GammaToken.sol\":15,\"src/interfaces/IGammaBeacon.sol\":17,\"src/interfaces/IGammaGame.sol\":12,\"src/interfaces/IGammaHook.sol\":13,\"src/libraries/GammaEmission.sol\":41,\"src/vendor/bls/BLS.sol\":634,\"src/vendor/bls/LICENSE\":21,\"src/vendor/bls/ModExp.sol\":643,\"src/vendor/bls/Precompiles.sol\":24,\"test/DrandBeacon.t.sol\":47,\"test/DrandBeaconAdversarial.t.sol\":76,\"test/GammaAccountingInvariant.t.sol\":327,\"test/GammaClaimBonus.t.sol\":237,\"test/GammaEmission.t.sol\":75,\"test/GammaFailurePaths.t.sol\":214,\"test/GammaGame.t.sol\":368,\"test/GammaHook.t.sol\":138,\"test/GammaInvariant.t.sol\":147,\"test/GammaLaunch.t.sol\":222,\"test/GammaRealBeacon.t.sol\":152,\"test/GammaRevision.t.sol\":42,\"test/GammaRevisionFailurePaths.t.sol\":32,\"test/GammaSettlementProperties.t.sol\":298,\"test/GammaStateMachine.t.sol\":487,\"test/utils/GammaFixture.sol\":198},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"da098f0b9c1095d1126a06ee77996ac0384f8127dd0c9e8271616e25813f1372","verifiedTreeHash":"61c0838a497edf32230e23f3a0ca570732a25660","verifierVersion":"0.1.0+a6ee6548"}]}