{"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":"3512e06f-1cd8-41ea-84f6-8e31b7bb985e","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"96fa003a918c72e7f351588aafcaa7d288e9ddab8b9b7f03d8de9421e9903360","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":"293754934d4edf858addc9af8c7eaef2b68095f16b8c53cbd64b693b9728dc86","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":"d19b709269958b9ce96406d31db184fad023410d193808251fc725ae4570d523","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":"4406a32c481eb75bbff8e00f37a37e0a0500ded4c65b4cad89226d5361cb2e25","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":"4a6bad216708a1c3798ce0e942326b39dfb99e9d36b6fc10adccc9b14386c7d0","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":"ea0d558de8df1902582a5dce73b5e872bdfdbca18841934ff248d6e1c261d859","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":"3b0d0c27b2c379e59df308398b25a07401ec98cf9c77ffb04f67f9cbf698a668","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":"ab035bc984b8c94ec4d88e101425fd8c06b8de4761f9d13e32293b6b39e3dd07","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"fb450ad3667c5933aee71a05b573e38464de2ffdc735437f8e3198f6ce90bda4","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":"Launch Gamma (GAMMA) with the Uniswap v4 hook in this repository, src/GammaHook.sol, without changing its logic. Constructor (poolManager 0x8366a39cc670b4001a1121b8f6a443a643e40951, imd 0x5f7bb59365ce557c26dbcaa4ee9d39a4b95b7127, game 0xb333a22c2c3dd0fe29fd1bf184c5ff2282148535). The hook charges every swap a fee that starts at 99% (9900 bps) at pool initialization and falls by 100 bps per minute to a floor of 3% (300 bps) from minute 96, forever. The fee is always taken in GAMMA (exact-input buys: from the output; sells: from the GAMMA input), and 100% is sent to the GammaGame contract as tonight's Harvest for the miners. Exact-output buys from anyone but the game revert with ExactOutputBuyNotSupported, because they are the only shape whose fee would be in IMD. The fee schedule is held at 99% until GammaGame.genesis() is set and decays from the later of pool creation and genesis. Swaps sent by the GammaGame contract are exempt. Hook permissions: beforeInitialize, afterInitialize, beforeSwap, afterSwap, beforeSwapReturnDelta, afterSwapReturnDelta. The hook has no owner-only functions and no backend signer. Pair with IMD. 10% of supply seeds the pool; the remaining 80% goes to the GammaGame contract as its Reserve.","parentJobId":null,"planHash":"29c970141006b4d44eaad08fcae2613a50e5bb2fe44133ed7dedd764bec9f6c1","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"3512e06f-1cd8-41ea-84f6-8e31b7bb985e","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-2232-launch-gamma-gamma-uniswap"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51050","feedbackHash":"fbcaf6d0303737e7d0bbc5d69e59013d23c0e25113a792b00f683589c000ce0b","nodeKey":"adapt_contract_project","submissionHash":"96fa003a918c72e7f351588aafcaa7d288e9ddab8b9b7f03d8de9421e9903360","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51880","feedbackHash":"22de975a2f605f3e7160e5419f834a172914c4070d6571bae15e03c706128519","nodeKey":"audit_economics","submissionHash":"293754934d4edf858addc9af8c7eaef2b68095f16b8c53cbd64b693b9728dc86","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52176","feedbackHash":"b70fd94d92ac3aa09d600455cef1bd06025c4af5b55aa5b31bbfba85030a7612","nodeKey":"audit_flow","submissionHash":"d19b709269958b9ce96406d31db184fad023410d193808251fc725ae4570d523","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51872","feedbackHash":"f4006b2b6774832d7777a9f189dc98c91610f02eb270a1063cdf4e9ca30af764","nodeKey":"audit_imported_code","submissionHash":"4406a32c481eb75bbff8e00f37a37e0a0500ded4c65b4cad89226d5361cb2e25","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51534","feedbackHash":"01c7e5a14178f29c371e0444bea23c73cb1ef53284d4728c541d47327d0098a6","nodeKey":"audit_judge","submissionHash":"4a6bad216708a1c3798ce0e942326b39dfb99e9d36b6fc10adccc9b14386c7d0","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52160","feedbackHash":"0be3d8116cdafb6e9091a4b2a2bfb25fe398f896cd95312c4dd7bf2aace520c7","nodeKey":"audit_math","submissionHash":"ea0d558de8df1902582a5dce73b5e872bdfdbca18841934ff248d6e1c261d859","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51537","feedbackHash":"5e8a962fa20b0516dcd1e34a1d21edb53fc60fe4a864c15dfc613d1575662ada","nodeKey":"audit_permissions","submissionHash":"3b0d0c27b2c379e59df308398b25a07401ec98cf9c77ffb04f67f9cbf698a668","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51341","feedbackHash":"e17c14f0783e063472a968acdbd01be249954055592ad1a232987a9aa5841f2d","nodeKey":"manifest","submissionHash":"ab035bc984b8c94ec4d88e101425fd8c06b8de4761f9d13e32293b6b39e3dd07","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51733","feedbackHash":"4f7b498ba43063e4ed10d97e1bc23392b9f88b728a6074a2d672c70ed7cf3339","nodeKey":"write_foundry_tests","submissionHash":"fb450ad3667c5933aee71a05b573e38464de2ffdc735437f8e3198f6ce90bda4","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"7129039d3fde04338d85ca4bf4d752d24fb40daed48aca924a03923bf4e5f227","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"09078b7cdfb673fe","findings":[{"citation":"resolved","description":"The brief's economic guarantee is that every GAMMA trade pays the schedule (99% falling to 3%) to the miners' Harvest and that the GAMMA/IMD pool is GAMMA's only market. The hook can only enforce that on swaps routed through its own PoolKey. Two legitimate v4 features defeat it with no privilege: (1) anyone can call PoolManager.initialize on a second PoolKey over the same two currencies with hooks = address(0) (or any other hook) on the same PoolManager, and every swap there pays zero Harvest fee; (2) inside the hooked pool itself, the hook enables no liquidity callbacks, so a GAMMA holder can park GAMMA as single-sided liquidity in a tick range just above the current price, let fee-paying buyers lift it, and withdraw IMD. The LP's GAMMA-to-IMD conversion is a sell that paid no sell fee (it even earned the 1.25% LP fee), while the Harvest received only the buyer-side fee. GAMMA enters circulation through game claims and the 10% distributor allocation, so both routes are available from day one and the sell side of the schedule is avoidable for anyone patient enough to LP. This is a design limitation of v4 hook fees, not an accounting bug, and it cannot be closed without changing the design (gating beforeAddLiquidity to the launch factory would close route 2 but not route 1; only token-level restrictions close route 1). Reported so the author can decide whether the stated guarantee should be narrowed in the README/brief or the hook's permission set widened before launch; the chosen permission set is a product decision.","line":69,"path":"src/GammaHook.sol","reproduction":"State: fixture pool GAMMA/IMD (fee 12500, spacing 60) initialized at 1:1 with 100,000,000e18 full-range liquidity, game initialized, 97 minutes after genesis so feeBps() == 300. Alice holds 10,000,000e18 GAMMA. Route 1: manager.initialize(PoolKey(currency0, currency1, 3000, 60, IHooks(address(0))), SQRT_PRICE_1_1); Alice adds 5,000,000e18 liquidity there; Bob swaps 1,000,000e18 GAMMA exact-input in that pool. Expected under the brief: 30,000e18 GAMMA to today's Harvest. Actual: getDay(today()).pot unchanged, game GAMMA balance unchanged, hook.pendingFees() == 0 (the same 1,000,000e18 sell through the hooked pool credits exactly 30,000e18). Route 2 (hooked pool): Alice calls lpRouter.modifyLiquidity(key, ModifyLiquidityParams(60, 600, 50_000_000e18, 0)) (ticks -600..-60 when IMD is currency0), which deposits 1,327,882.796e18 GAMMA and 0 IMD; Bob buys with 10,000,000e18 IMD exact-input (pays 274,924.97e18 GAMMA buy fee to the Harvest); Alice removes the position and receives 1,389,804.27e18 IMD and 0 GAMMA. Expected under the brief: Alice's GAMMA->IMD conversion pays 3% (about 39,836e18 GAMMA) to the Harvest. Actual: Harvest delta equals the buyer-side fee only; Alice's leg paid nothing. Both runs are in test/scratch/FeeBypass.t.sol (test_hooklessPoolSellPaysNothing, test_lpRouteSellsGammaWithoutSellFee).","severity":"low","snippet":"            beforeAddLiquidity: false,\n            afterAddLiquidity: false,\n            beforeRemoveLiquidity: false,\n            afterRemoveLiquidity: false,","title":"The Harvest fee binds only to swaps inside the hooked pool: GAMMA converts to IMD fee-free through a hookless pool or by providing range liquidity"},{"citation":"resolved","description":"When a sell's fee exceeds the PoolManager's current GAMMA balance (reachable after heavy buying has drained the pool's GAMMA), the fee is held as an ERC-6909 claim and only reaches the game when redeemFees() is called. The game funds _days[today()] on receipt, so the day whose failed miners share that fee is decided by the redeemer's timing rather than by the swap. Because redemption is permissionless and any observer can call it immediately, the only lever is delay while nobody else acts; the amount can never be redirected or lost. Documented behaviour, recorded here as a trust/operations note: the frontend or a keeper should call redeemFees() whenever pendingFees() > 0 so the Harvest attribution matches the trading day.","line":183,"path":"src/GammaHook.sol","reproduction":"Fixture: after _drainGammaByBuying() (Bob buys with 1e29 IMD, leaving the manager under 1,200,000e18 GAMMA), Alice sells 40,000,000e18 GAMMA exact-input at the 3% floor. afterSwap defers 1,200,000e18 (hook.pendingFees() == 1,200,000e18, game balance and today's pot unchanged). Warp to the next day and call hook.redeemFees(): getDay(today()).pot rises by 1,200,000e18 on the new day while the swap's day received nothing. This is the existing test test_deferredFeeCreditsHarvestWhenDelivered in test/GammaHookAudit.t.sol.","severity":"info","snippet":"    /// @notice Anyone may redeem deferred fees after the swap's unlock completes. All GAMMA goes to the game;\n    /// the game credits the Harvest on receipt (day 0 if redeemed before genesis). No caller can redirect it.","title":"A deferred sell fee is credited to the Harvest of whichever day someone chooses to redeem it, not the day of the swap"}],"hash":"293754934d4edf858addc9af8c7eaef2b68095f16b8c53cbd64b693b9728dc86","nodeId":"8e430284-1785-4f76-a0aa-0c3b7a57ed3d","outcome":"completed","summary":"The review is complete and `.imd-findings.json` is written and validated against the tree. Here is the recap.\n\n**Outcome.** The hook's fee economics hold up. I traced every swap shape in both currency orderings and found the delta accounting, fee schedule, game exemption, deferred-claim path and pre-genesis handling all behave as the brief states. All 163 existing tests pass on this tree. I found no critical, high or medium defect, so no proof files are attached.\n\n**Findings written:**\n\n- **Low: the Harvest fee only binds inside the hooked pool.** Anyone can open a second GAMMA/IMD pool keyed to no hook on the same PoolManager and sell there with zero fee. Inside the hooked pool, a GAMMA holder can park single-sided liquidity just above the price, let fee-paying buyers lift it, and withdraw IMD with no sell fee. My scratch test converted about 1.33M GAMMA into 1.39M IMD this way while the Harvest only saw the buyer's fee. This is a v4 design limit rather than an accounting bug. The hook enables no liquidity callbacks, so the author's choice is whether to narrow the stated guarantee or widen the permission set.\n- **Info: a deferred sell fee lands on the Harvest day of whoever redeems it.** Documented behaviour, recorded as an operations note so a keeper redeems promptly.\n\n**Coverage.** All 18 listed entry points have a verdict, plus four invariant rows. One row is marked unreached: I did not fork chain 4663 to confirm the live IMD token is a plain ERC-20 or that the game at the named address matches this source. The hook never moves IMD, so that gap only affects the game's energy purchases.\n\n**What I checked specifically from the Economic, Invariant and Flow Gap guides:** fee pushed to zero (dust only), fee at max (checked casts, no overflow within supply), stale rate between beforeSwap and afterSwap (same value, genesis cannot change mid-swap), reentrancy through onHookFee and the game's unlock (exempt path avoids the nonReentrant clash), phantom credit before settlement (credit only persists if the unlock settles), double counting across direct, deferred, pre-genesis surplus and sweep paths (none), and manager solvency for take versus mint.\n\nThe scratch test lives in test/scratch/FeeBypass.t.sol and nothing outside the findings file and that scratch directory was touched.","treeHash":null,"usage":{"cachedInputTokens":846783,"inputTokens":226,"model":"claude-fable-5-1","outputTokens":41077,"runtime":"claude","turns":24,"wallClockMs":1066394}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"1da032f42d59f73b","findings":[{"citation":"resolved","description":"Trust gap (access x asymmetry). The brief says the hook has no owner, and the README says the configurator 'has no other power' than one initialize call. But the hook trusts game.genesis() != 0 as proof that the game is wired to *this* hook, and it calls onHookFee without a fallback. GammaGame.initialize (src/GammaGame.sol:205-206) only checks that IGammaHook(hook).game()==this and IGammaHook(hook).poolId()==keccak256(key), which a decoy contract can return for any key. Neither the decoy nor its pool has to exist in the PoolManager. Because the decoy key can name the real GAMMA, the 800M Reserve the factory sends passes the ReserveNotFunded check. After that, genesis != 0 and game.hook = decoy, so every non-game swap on the real launch pool with a non-zero fee calls game.onHookFee from GammaHook. That call reverts NotHook (src/GammaGame.sol:309), and the revert takes the whole swap with it. The fee is always at least 3%, so the GAMMA/IMD market is frozen for good, and buyEnergies swaps against the decoy key fail too (pool not initialized), which also locks the Reserve. The README's trust note only covers a 'fake hook and token'. It does not say that a decoy hook over the real token turns the configurator's one call into a permanent kill switch over this hook, which is an account outside this launch's owner model (configurator = $owner of launch 2215). The inaction variant has the same root: if the configurator never calls initialize, feeBps() returns 9900 forever and the Reserve and pre-genesis fees stay locked. Fix that keeps the design: make the report best-effort, e.g. `try game.onHookFee(gamma, amount) {} catch {}`. The GAMMA has already arrived by take(), and GammaGame.sweep() credits any uncredited balance to today's Harvest. Alternatively, call onHookFee only when the game's hook() equals address(this) (add hook() to IGammaGame). Either way a mis-wired game can no longer halt trading.","line":180,"path":"src/GammaHook.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, SwapParams} from \"v4-core/src/types/PoolOperation.sol\";\nimport {PoolModifyLiquidityTest} from \"v4-core/src/test/PoolModifyLiquidityTest.sol\";\nimport {PoolSwapTest} from \"v4-core/src/test/PoolSwapTest.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\";\nimport {IGammaGame} from \"src/interfaces/IGammaGame.sol\";\n\ncontract NullBeacon is IGammaBeacon {\n    function genesis() external pure returns (uint64) { return 1727521075; }\n    function period() external pure returns (uint64) { return 3; }\n    function randomness(uint64) external pure returns (bytes32) { return bytes32(0); }\n    function prove(uint64, bytes calldata) external pure returns (bytes32) { return bytes32(0); }\n}\n\n/// @dev Not a hook and never deployed into any pool: it only answers the two views GammaGame.initialize reads.\ncontract DecoyHook {\n    IGammaGame public game;\n    bytes32 public poolId;\n    constructor(IGammaGame g) { game = g; }\n    function setPoolId(bytes32 id) external { poolId = id; }\n}\n\ncontract ConfiguratorBricksHookTest is Test {\n    PoolManager manager;\n    PoolModifyLiquidityTest lpRouter;\n    PoolSwapTest swapRouter;\n    MockERC20 imd;\n    GammaToken gamma;\n    GammaGame game;\n    GammaHook hook;\n    PoolKey key;\n    bool gammaIs0;\n    address configurator = makeAddr(\"configurator\");\n    address bob = makeAddr(\"bob\");\n\n    function setUp() public {\n        vm.warp(1_760_000_000);\n        manager = new PoolManager(address(this));\n        lpRouter = new PoolModifyLiquidityTest(manager);\n        swapRouter = new PoolSwapTest(manager);\n        imd = new MockERC20(\"Identity.md\", \"IMD\", 18);\n        gamma = new GammaToken();\n        game = new GammaGame(IPoolManager(address(manager)), ERC20(address(imd)), new NullBeacon(), configurator);\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        hook = GammaHook(hookAddr);\n\n        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: 12_500,\n            tickSpacing: 60,\n            hooks: IHooks(hookAddr)\n        });\n        // Launch: the factory initializes the real pool, seeds it and sends the 800M Reserve to the game.\n        manager.initialize(key, 79228162514264337593543950336);\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(60),\n                tickUpper: TickMath.maxUsableTick(60),\n                liquidityDelta: 100_000_000e18,\n                salt: 0\n            }),\n            \"\"\n        );\n        imd.mint(bob, 1_000_000e18);\n    }\n\n    function _buy(uint256 imdIn) internal {\n        vm.startPrank(bob);\n        imd.approve(address(swapRouter), type(uint256).max);\n        bool zeroForOne = !gammaIs0;\n        swapRouter.swap(\n            key,\n            SwapParams({\n                zeroForOne: zeroForOne,\n                amountSpecified: -int256(imdIn),\n                sqrtPriceLimitX96: zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1\n            }),\n            PoolSwapTest.TestSettings({takeClaims: false, settleUsingBurn: false}),\n            \"\"\n        );\n        vm.stopPrank();\n    }\n\n    /// The 2215 configurator wires the game to a decoy hook over the REAL GAMMA (the real Reserve passes the balance\n    /// check). GammaHook has no way to see this: genesis() is now non-zero, so every fee-bearing swap on the launched\n    /// pool calls game.onHookFee, which reverts NotHook. The launch pool can never trade again.\n    function test_configuratorWiringToDecoyHookFreezesEveryFeeBearingSwap() public {\n        _buy(1_000e18); // trading works before the game is wired\n\n        DecoyHook decoy = new DecoyHook(IGammaGame(address(game)));\n        PoolKey memory fake = PoolKey({\n            currency0: key.currency0,\n            currency1: key.currency1,\n            fee: 3000, // any key that is not the launch pool's\n            tickSpacing: 60,\n            hooks: IHooks(address(decoy))\n        });\n        decoy.setPoolId(keccak256(abi.encode(fake)));\n        vm.prank(configurator);\n        game.initialize(fake);\n        assertEq(game.hook(), address(decoy));\n        assertEq(address(game.gamma()), address(gamma), \"the real GAMMA and real Reserve were accepted\");\n\n        vm.warp(block.timestamp + 2 hours);\n        // Expected: a buy on the launched GAMMA/IMD pool still pays its 3% fee to the game and succeeds.\n        // Actual: GammaHook._sendToGame -> GammaGame.onHookFee reverts NotHook, reverting the whole swap.\n        _buy(1_000e18);\n    }\n}","reproduction":"1) Launch as the factory does: deploy GammaHook(pm, imd, game), initialize the GAMMA/IMD pool (fee 12500, tickSpacing 60), seed liquidity and send 800M GAMMA to the game. 2) A buy with 1,000 IMD exact input succeeds (pre-genesis, 99% fee). 3) The configurator deploys DecoyHook{game()=game, poolId()=keccak256(fakeKey)} with fakeKey = {same GAMMA/IMD currencies, fee 3000, tickSpacing 60, hooks: decoy}, then calls game.initialize(fakeKey). This succeeds: game.gamma()==real GAMMA and game.hook()==decoy. 4) Warp 2 hours and buy again with 1,000 IMD exact input on the real pool. Expected: the swap succeeds and 3% of the output goes to the game. Actual: it reverts with WrappedError(hook, afterSwap 0xb47b2fb1, NotHook 0xc72cfc38, HookCallFailed 0xa9e35b2f). Sells and exact-output sells revert the same way. `forge test --match-path test/scratch/ConfiguratorBricksHook.t.sol` fails on the current code for this reason, and it passes once onHookFee is best-effort or gated on game.hook()==address(this).","severity":"medium","snippet":"        if (game.genesis() != 0) game.onHookFee(gamma, amount);","title":"GammaHook's liveness depends on the 2215 configurator: wiring the game to a decoy hook over the real GAMMA makes every fee-bearing swap on the launch pool revert forever (NotHook)"}],"hash":"3b0d0c27b2c379e59df308398b25a07401ec98cf9c77ffb04f67f9cbf698a668","nodeId":"eea37536-900d-431d-9c0e-d6859f1d7b96","outcome":"completed","summary":"I found one defect, rated medium. A reproduction test fails on the current code, and I confirmed it passes once a fix is applied. The findings and a coverage row for every entry point are in `.imd-findings.json`. I changed no files: I briefly patched `src/GammaHook.sol` to test the fix and restored it with `git checkout`, and `git status` is clean.\n\n**Finding 1 (medium): one call by the game's configurator can freeze trading on the GAMMA pool for good**\n- **What goes wrong:** once `game.genesis() != 0`, the hook calls `game.onHookFee` on every fee-bearing swap and has no fallback if it fails (`src/GammaHook.sol:180`).\n- **Why it can happen:** `GammaGame.initialize` (`src/GammaGame.sol:205-206`) only asks the hook it is given which game and pool it belongs to. A fake contract can give the answers it expects for a fake pool key. That fake key can still name the real GAMMA token, so the real 800M Reserve passes the balance check.\n- **Result:** the game is wired to the fake hook. Every swap on the real pool pays at least a 3% fee, so every swap reverts with `NotHook()` and the market stops for good. The game's own `buyEnergies` also fails, because the fake pool doesn't exist, so the Reserve is locked too.\n- **Who holds the power:** the configurator is the owner of the earlier launch 2215, not part of this launch. The README says the configurator has \"no other power\" than one `initialize` call. Its trust note covers a fake hook with a fake token, but not a fake hook with the real token, which is what bricks this hook.\n- **Same root, other direction:** if the configurator never calls `initialize`, the fee stays at 99% forever and the Reserve stays locked.\n- **Proof:** `test/scratch/ConfiguratorBricksHook.t.sol` fails now with that `NotHook()` revert.\n- **Fix:** make the report best-effort with `try game.onHookFee(gamma, amount) {} catch {}`. The tokens have already arrived by then, and anyone can credit them to the Harvest with `GammaGame.sweep()`. With that change, the test passes.\n\n**Coverage:** all 18 entry points have a row, plus three invariants I checked. Apart from the finding, everything I traced holds:\n- **Pool setup:** `beforeInitialize` allows only one pool, which must be paired with IMD. Because the factory deploys the hook and initializes the pool in the same transaction, nobody can initialize it first.\n- **Callers:** every hook callback accepts calls only from the PoolManager, and the two unlock callbacks can only be reached through each contract's own `unlock`.\n- **Game exemption:** it is checked the same way in `beforeSwap` and `afterSwap`. It can only be used through `buyEnergies`, which burns the GAMMA it buys, so it can't be used to get GAMMA without paying the fee.\n- **Fees:** `redeemFees` can only pay the fixed game address. Exact-output buys are refused, so every fee is taken in GAMMA.\n- **Game and token:** the claim owner checks and the GammaToken functions have no privileged path.\n\nI did not run the protected test suites.","treeHash":null,"usage":{"cachedInputTokens":1149762,"inputTokens":28,"model":"claude-opus-5-5","outputTokens":24671,"runtime":"claude","turns":15,"wallClockMs":377323}},{"artifacts":[],"attempt":2,"bundleHash":null,"device":"f5666f1d1aa75678","findings":[{"citation":"resolved","description":"For an exact-input sell, beforeSwap takes feeBps of the whole GAMMA input and swaps the rest, so the Harvest receives feeBps of everything the seller gives up (99% of 1,000,000 GAMMA = 990,000 in the first minute). For an exact-output sell (GAMMA in, exact IMD out, amountSpecified > 0) nothing is charged in beforeSwap and afterSwap computes fee = |delta.gammaIn| * feeBps / BPS on the amount the pool consumed, then adds it on top via the unspecified return delta. The seller therefore pays I + f*I in total and the Harvest gets f*I, an effective rate of f/(1+f): 4974 bps instead of 9900 bps during the launch window, 291 bps instead of 300 bps at the floor. Anyone holding GAMMA at launch (the swarm's 10%, early buyers) can sell during the 99% sniper window by asking for an exact IMD amount and keep roughly half of what the schedule says belongs to the miners; the same IMD bought through an exact-input sell costs about 100x more GAMMA. The brief's guarantee ('sells: the fee is taken from the GAMMA input' at feeBps) is broken for one of the two sell shapes. Fix: for an exact-output sell compute the fee on the gross amount, fee = in * feeBps / (BPS - feeBps), or refuse exact-output sells as exact-output buys are refused.","line":141,"path":"src/GammaHook.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, SwapParams} from \"v4-core/src/types/PoolOperation.sol\";\nimport {PoolModifyLiquidityTest} from \"v4-core/src/test/PoolModifyLiquidityTest.sol\";\nimport {PoolSwapTest} from \"v4-core/src/test/PoolSwapTest.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 ScratchBeacon 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 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 An exact-output sell (GAMMA in, exact IMD out) is charged feeBps of the GAMMA the pool consumed,\n/// not feeBps of the GAMMA the seller gives up. The seller's effective rate is f/(1+f): 49.7% instead of\n/// 99% in the launch window, 2.91% instead of 3% afterwards. The same IMD bought through an exact-input\n/// sell is charged the full schedule.\ncontract GammaHookExactOutSellFeeTest 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    PoolSwapTest swapRouter;\n    MockERC20 imd;\n    GammaToken gamma;\n    ScratchBeacon beacon;\n    GammaGame game;\n    GammaHook hook;\n    PoolKey key;\n    bool gammaIs0;\n\n    address alice = makeAddr(\"alice\");\n    address bob = makeAddr(\"bob\");\n\n    function setUp() public {\n        vm.warp(1_760_000_000);\n\n        manager = new PoolManager(address(this));\n        lpRouter = new PoolModifyLiquidityTest(manager);\n        swapRouter = new PoolSwapTest(manager);\n        imd = new MockERC20(\"Identity.md\", \"IMD\", 18);\n        beacon = new ScratchBeacon();\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        hook = GammaHook(hookAddr);\n\n        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        // Two sellers holding GAMMA at launch (e.g. the swarm's 10%).\n        gamma.transfer(alice, 10_000_000e18);\n        gamma.transfer(bob, 10_000_000e18);\n    }\n\n    function sellExactIn(address who, uint256 gammaIn) internal {\n        vm.startPrank(who);\n        gamma.approve(address(swapRouter), type(uint256).max);\n        bool zeroForOne = gammaIs0;\n        swapRouter.swap(\n            key,\n            SwapParams({\n                zeroForOne: zeroForOne,\n                amountSpecified: -int256(gammaIn),\n                sqrtPriceLimitX96: zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1\n            }),\n            PoolSwapTest.TestSettings({takeClaims: false, settleUsingBurn: false}),\n            \"\"\n        );\n        vm.stopPrank();\n    }\n\n    function sellExactOut(address who, uint256 imdOut) internal {\n        vm.startPrank(who);\n        gamma.approve(address(swapRouter), type(uint256).max);\n        bool zeroForOne = gammaIs0;\n        swapRouter.swap(\n            key,\n            SwapParams({\n                zeroForOne: zeroForOne,\n                amountSpecified: int256(imdOut),\n                sqrtPriceLimitX96: zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1\n            }),\n            PoolSwapTest.TestSettings({takeClaims: false, settleUsingBurn: false}),\n            \"\"\n        );\n        vm.stopPrank();\n    }\n\n    /// @dev In the first minute the fee is 99%. An exact-input seller of 1,000,000 GAMMA pays 990,000 to the\n    /// Harvest. An exact-output seller asking for the same IMD gives up far less GAMMA and pays about half of\n    /// what leaves their wallet, not 99% of it.\n    function test_exactOutputSellPaysTheFeeSchedule() public {\n        vm.warp(hook.launchedAt()); // launch window: 99%\n        assertEq(hook.feeBps(), 9_900);\n\n        uint256 d = game.today();\n        uint256 potBefore = game.getDay(d).pot;\n        uint256 aliceGammaBefore = gamma.balanceOf(alice);\n        sellExactIn(alice, 1_000_000e18);\n        uint256 imdGot = imd.balanceOf(alice);\n        uint256 aliceFee = game.getDay(d).pot - potBefore;\n        uint256 alicePaid = aliceGammaBefore - gamma.balanceOf(alice);\n        assertEq(aliceFee, 990_000e18, \"exact-input sell: 99% of the GAMMA given up\");\n        assertEq(alicePaid, 1_000_000e18);\n\n        // Bob asks for exactly the IMD Alice received.\n        potBefore = game.getDay(d).pot;\n        uint256 bobGammaBefore = gamma.balanceOf(bob);\n        sellExactOut(bob, imdGot);\n        uint256 bobFee = game.getDay(d).pot - potBefore;\n        uint256 bobPaid = bobGammaBefore - gamma.balanceOf(bob);\n\n        // The schedule says 99% of what the seller gives up goes to the Harvest, whichever swap shape is used.\n        uint256 bobRateBps = bobFee * 10_000 / bobPaid;\n        assertApproxEqAbs(bobRateBps, 9_900, 50, \"exact-output sell is charged f/(1+f), about 49.7%, not 99%\");\n        // And the same IMD should cost about the same GAMMA either way (modulo price impact of the first sell).\n        assertGe(bobPaid * 10, alicePaid, \"exact-output sell bought the same IMD for a tenth of the GAMMA\");\n    }\n}","reproduction":"Fresh GAMMA/IMD pool at 1:1, game initialized, block.timestamp == hook.launchedAt() so feeBps() == 9900. (1) Alice swaps exact-input: zeroForOne = gammaIs0, amountSpecified = -1_000_000e18. Harvest pot grows by 990_000e18 (99% of what Alice gave up); Alice receives X IMD. (2) Bob swaps exact-output for the same X IMD: zeroForOne = gammaIs0, amountSpecified = +X. Expected: Bob gives up about the same GAMMA as Alice and 99% of it reaches the Harvest. Actual: Bob gives up about 1/50 of Alice's GAMMA and the Harvest receives 4974 bps of what he gave up (assertion '4974 !~= 9900'). Test: test/scratch/GammaHookExactOutSellFee.t.sol, test_exactOutputSellPaysTheFeeSchedule.","severity":"medium","snippet":"        uint256 fee = uint256(uint128(amount)) * feeBps() / BPS;","title":"Exact-output sells are charged feeBps of the swapped input, not of the GAMMA the seller gives up: effective rate f/(1+f) (49.7% instead of 99% in the launch window)"},{"citation":"resolved","description":"beforeSwap charges the sell fee on params.amountSpecified, the amount the seller offered, and v4 then swaps only up to the seller's sqrtPriceLimitX96. When the limit is reached early the swapper's delta is the small swapped amount plus the full fee: the Harvest receives feeBps of GAMMA that was never traded. Exact-input buys, by contrast, are charged in afterSwap on the realised output. A seller using a protective price limit (or one whose limit is approached by a front-running sell in the same block) pays a fee many times the value traded; the GAMMA is not lost to an attacker but goes to the Harvest, so the seller cannot recover it. Fix: charge the sell fee in afterSwap on the realised GAMMA input (abs of the specified delta), as the exact-output path already does, or document that sells must use the pool's MIN/MAX limits.","line":113,"path":"src/GammaHook.sol","reproduction":"Pool at 1:1 with 1e26 full-range liquidity, game initialized, block.timestamp >= launchedAt + 96 minutes so feeBps() == 300. Alice holds 50,000,000e18 GAMMA and swaps exact-input: zeroForOne = gammaIs0, amountSpecified = -10_000_000e18, sqrtPriceLimitX96 = current price moved 1 bp towards the sell direction (price - price/10000 when gammaIs0, else price + price/10000). Expected: a fee of 3% of what was actually swapped. Actual: 10_126.58e18 GAMMA are swapped, but the Harvest pot grows by 300_000e18 (3% of the full 10M) and Alice's GAMMA balance falls by 310_126.58e18; the fee is ~30x the amount traded. Demonstrated by test/scratch/GammaHookLow.t.sol test_partialFillSellPaysFeeOnTheWholeInput (passes on current code, logging the two amounts).","severity":"low","snippet":"            uint256 fee = uint256(-params.amountSpecified) * feeBps() / BPS;","title":"Exact-input sells are charged feeBps of the whole requested input even when a price limit stops the swap after a sliver"},{"citation":"resolved","description":"_collect reports every fee to the game, and GammaGame.onHookFee (src/GammaGame.sol:309-311) reverts NotHook while game.hook is address(0) and FeeNotReceived while game.gamma is address(0); both are only set by initialize(key), which the configurator EOA calls after the hook launch. Until then every swap not sent by the game reverts (fee > 0 for any amount >= 2 wei), although feeBps() reports 9900 and the brief says the schedule is 'held at 99%' while genesis is unset. The decision to freeze is documented in ADAPTATION.md, but two consequences matter for the adapter: (1) trading start, and so the sniper fee window, is controlled by one externally owned key, which the brief's 'no owner-only functions' does not cover; (2) if initialize can never succeed (RESERVE 800_000_000e18 exactly equals the 80% remainder, so any shortfall of a single wei at the game, or a key that differs from the hook's poolId, makes ReserveNotFunded/BadPool permanent) the pool can never trade and the 10% seeded liquidity is stranded except through the game's own exempt swaps, which also need initialize. The adapter should verify the remainder sent to the game is >= RESERVE and have the configurator's initialize scripted immediately after the launch transaction.","line":153,"path":"src/GammaHook.sol","reproduction":"Deploy PoolManager, IMD mock, GammaToken, GammaGame(manager, imd, beacon, configurator) and GammaHook(manager, imd, game) at an address carrying the six flags; initialize the GAMMA/IMD pool at 1:1 and seed full-range liquidity; do NOT call game.initialize. hook.feeBps() returns 9900 and game.genesis() returns 0. Bob swaps exact-input 1_000e18 IMD for GAMMA through PoolSwapTest; Alice swaps exact-input 1_000e18 GAMMA for IMD. Expected per the brief: both trade and pay 99%. Actual: both revert (the PoolManager's WrappedError wrapping GammaGame.NotHook, selector 0xa9e35b2f). Demonstrated by test/scratch/GammaHookLow.t.sol test_poolFrozenUntilGameInitialize.","severity":"low","snippet":"        game.onHookFee(currency, amount);","title":"Every non-game swap reverts until the configurator calls GammaGame.initialize: the pool is frozen, not 'held at 99%' as the brief says"},{"citation":"resolved","description":"poolManager.take transfers GAMMA to the game immediately, inside beforeSwap (exact-input sells) or afterSwap (exact-output sells), before the swapper's router has settled its GAMMA input. The transfer is funded by whatever GAMMA the PoolManager already holds. GAMMA exists only in this pool, so once buys have moved most of the seeded GAMMA out of the manager, any sell whose fee (3% floor, up to 99% in the launch window) exceeds the remaining balance reverts in the ERC-20 transfer, although the sell itself would succeed and would refill the manager. Sellers must split into sizes whose fee fits the manager's current GAMMA balance, which no router does automatically. Fix: mint an ERC-6909 claim to the game (poolManager.mint) when the manager's balance is short, or take the fee in afterSwap only after checking currency.balanceOf(poolManager), as the launch reference describes for fees paid during the swap.","line":152,"path":"src/GammaHook.sol","reproduction":"Pool at 1:1 with 1e26 full-range liquidity, game initialized, block.timestamp >= launchedAt + 96 minutes (3% fee). Bob swaps exact-input 1e29 IMD for GAMMA (zeroForOne = !gammaIs0, limit MAX/MIN). The PoolManager is left holding 101_163.38e18 GAMMA. Alice (50,000,000e18 GAMMA) swaps exact-input 40_000_000e18 GAMMA for IMD with the pool's MIN/MAX price limit. Expected: the sell executes, fee 1_200_000e18 to the Harvest. Actual: the swap reverts inside poolManager.take (TRANSFER_FAILED: 1_200_000e18 > 101_163e18 held). A sell of 1_000e18 in the same state succeeds. Demonstrated by test/scratch/GammaHookLow.t.sol test_largeSellRevertsWhenManagerHoldsLessGammaThanTheFee.","severity":"low","snippet":"        poolManager.take(currency, address(game), amount);","title":"Sell fees are taken from the PoolManager's GAMMA balance before the seller settles: a sell whose fee exceeds the manager's GAMMA reverts"},{"citation":"resolved","description":"Read in full: src/GammaHook.sol (the audited hook), src/GammaGame.sol (fee recipient: initialize, onHookFee, unlockCallback, settlement and claim paths as they interact with the hook), src/GammaToken.sol, src/interfaces/IGammaGame.sol, src/interfaces/IGammaHook.sol, src/libraries/GammaEmission.sol (RESERVE = 800_000_000e18), test/utils/GammaFixture.sol, test/GammaHook.t.sol, launch/02-hook.json, launch/2215-game-manifest.json, README.md, the relevant parts of ADAPTATION.md, the three protected tests and lib/v4-core Hooks/PoolManager delta handling. Not reached: the live contracts on chain 4663 (PoolManager 0x8366a39cc670b4001a1121b8f6a443a643e40951, IMD 0x5f7bb59365ce557c26dbcaa4ee9d39a4b95b7127, the deployed GammaGame 0xb333a22c2c3dd0fe29fd1bf184c5ff2282148535): no RPC or fork was used, so their bytecode was assumed to match this tree's GammaGame (constructor args in launch/2215-game-manifest.json agree with the hook's expectations: genesis() and onHookFee(Currency,uint256)). src/DrandBeacon.sol, src/vendor/bls, frontend/ and script/ were not reviewed (not in the hook's call graph). Checks that passed on the hook: every callback is gated by onlyPoolManager; getHookPermissions matches the six declared flags and is validated in the constructor; beforeInitialize/afterInitialize bind exactly one IMD pool and return the right selectors; the unused callbacks revert; delta accounting nets to zero on every path (beforeSwapDelta +fee specified vs take, afterSwap +fee unspecified vs take); exact-output buys from anyone but the game revert; game-sent swaps are exempt; fee rounding favours the swapper by < 1 wei; no owner, mint, pause, blacklist, DELEGATECALL, SELFDESTRUCT, proxy or post-deploy configuration in the hook; GAMMA is a plain ERC-20 so no fee-on-transfer or reentrancy via the token; onHookFee is nonReentrant and only credits GAMMA that arrived. The constructor takes the PoolManager as its first argument, so the manifest must write it as $poolManager rather than the literal 0x8366... the brief quotes; imd and game are literal addresses in declaration order (poolManager, imd, game). The reference's preference for ETH/pair-side fees conflicts with the brief's 'fee always in GAMMA' and the brief wins, so it is not reported as a defect.","line":20,"path":"src/GammaHook.sol","reproduction":"Not a defect; the record of what was and was not inspected, required by the acceptance criteria.","severity":"info","snippet":"contract GammaHook is IHooks {","title":"Scope: contracts read, contracts not reachable, and checks that passed"}],"hash":"4406a32c481eb75bbff8e00f37a37e0a0500ded4c65b4cad89226d5361cb2e25","nodeId":"8980791f-0d44-4ea6-8191-d18ad97ba542","outcome":"completed","summary":"The review is complete. Findings are in `.imd-findings.json` at the repository root, and no repository file was changed. The scratch tests live under `test/scratch/`, which is not kept.\n\n**Result: one medium defect, three low ones, no critical or high.** The hook's access control, permission flags, delta accounting, exact-output buy refusal, game exemption and absence of owner, mint, pause, proxy or forbidden opcodes all check out.\n\nFindings, most severe first:\n\n- **Medium, with proof.** Exact-output sells pay the fee on the GAMMA the pool consumed, not on the GAMMA the seller gives up. The effective rate is f/(1+f). In the 99% launch window a seller asking for an exact IMD amount pays about 49.7% of what leaves their wallet, and buys the same IMD for roughly 1/50 of the GAMMA an exact-input seller needs. The proof test at `test/scratch/GammaHookExactOutSellFee.t.sol` fails on the current code with 4974 bps measured against 9900 expected.\n- **Low.** An exact-input sell with a protective price limit is charged 3% of the whole offered input even when only a sliver swaps. Reproduced: 10,126 GAMMA traded, 300,000 GAMMA fee.\n- **Low.** Until the configurator calls the game's initialize, every non-game swap reverts with the game's NotHook error. The brief says the fee is held at 99%, but the pool is frozen, and trading start depends on one externally owned key.\n- **Low.** Sell fees are taken from the PoolManager's GAMMA balance before the seller settles. After large buys drain the pool side, a sell whose fee exceeds the manager's remaining GAMMA reverts. Reproduced: 40M GAMMA sell reverts while the manager holds 101k GAMMA.\n- **Info.** Scope record: read the hook, the game, the token, interfaces, emission library, fixtures, manifests and protected tests. Not reached: the live PoolManager, IMD token and deployed GammaGame on chain 4663, since no RPC or fork was used.\n\nOne note for the adapter: the constructor's first argument must be written as `$poolManager` in the manifest, not the literal address the brief quotes.","treeHash":null,"usage":{"cachedInputTokens":1556221,"inputTokens":482,"model":"claude-fable-5-1","outputTokens":35367,"runtime":"claude","turns":26,"wallClockMs":635751}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"739094f0e08668ee","findings":[{"citation":"resolved","description":"Merged from audit_permissions (one finding). _sendToGame treats game.genesis() != 0 as proof that the game is wired to this hook and calls game.onHookFee with no fallback. GammaGame.onHookFee reverts NotHook unless msg.sender == game.hook. GammaGame.initialize only checks IGammaHook(hook).game() == this and IGammaHook(hook).poolId() == keccak256(key), which a decoy contract can return for any key. The decoy key can name the real GAMMA, which the game already holds as its 800M Reserve. Once the game is initialized with a decoy, genesis != 0 and game.hook != this hook. From then on every non-game swap with a non-zero fee (the fee is at least 3% forever) reverts inside afterSwap, so the GAMMA/IMD market freezes permanently. The hook has no owner and cannot be repaired. This needs a deliberate act by the game's configurator, a trusted party: a mistyped key fails the poolId check. The README lists a crafted fake hook as a configurator trust assumption, but not that it would permanently halt this ownerless hook's market. Severity is low because only that trusted actor can trigger it. Fix that keeps the design: make the report best-effort (`try game.onHookFee(gamma, amount) {} catch {}`). The GAMMA is already in the game, and GammaGame.sweep() credits any uncredited balance to today's Harvest.","line":180,"path":"src/GammaHook.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, SwapParams} from \"v4-core/src/types/PoolOperation.sol\";\nimport {PoolModifyLiquidityTest} from \"v4-core/src/test/PoolModifyLiquidityTest.sol\";\nimport {PoolSwapTest} from \"v4-core/src/test/PoolSwapTest.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\";\nimport {IGammaGame} from \"src/interfaces/IGammaGame.sol\";\n\ncontract NullBeacon is IGammaBeacon {\n    function genesis() external pure returns (uint64) { return 1727521075; }\n    function period() external pure returns (uint64) { return 3; }\n    function randomness(uint64) external pure returns (bytes32) { return bytes32(0); }\n    function prove(uint64, bytes calldata) external pure returns (bytes32) { return bytes32(0); }\n}\n\n/// @dev Not a hook and never deployed into any pool: it only answers the two views GammaGame.initialize reads.\ncontract DecoyHook {\n    IGammaGame public game;\n    bytes32 public poolId;\n    constructor(IGammaGame g) { game = g; }\n    function setPoolId(bytes32 id) external { poolId = id; }\n}\n\ncontract ConfiguratorBricksHookTest is Test {\n    PoolManager manager;\n    PoolModifyLiquidityTest lpRouter;\n    PoolSwapTest swapRouter;\n    MockERC20 imd;\n    GammaToken gamma;\n    GammaGame game;\n    GammaHook hook;\n    PoolKey key;\n    bool gammaIs0;\n    address configurator = makeAddr(\"configurator\");\n    address bob = makeAddr(\"bob\");\n\n    function setUp() public {\n        vm.warp(1_760_000_000);\n        manager = new PoolManager(address(this));\n        lpRouter = new PoolModifyLiquidityTest(manager);\n        swapRouter = new PoolSwapTest(manager);\n        imd = new MockERC20(\"Identity.md\", \"IMD\", 18);\n        gamma = new GammaToken();\n        game = new GammaGame(IPoolManager(address(manager)), ERC20(address(imd)), new NullBeacon(), configurator);\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        hook = GammaHook(hookAddr);\n\n        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: 12_500,\n            tickSpacing: 60,\n            hooks: IHooks(hookAddr)\n        });\n        // Launch: the factory initializes the real pool, seeds it and sends the 800M Reserve to the game.\n        manager.initialize(key, 79228162514264337593543950336);\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(60),\n                tickUpper: TickMath.maxUsableTick(60),\n                liquidityDelta: 100_000_000e18,\n                salt: 0\n            }),\n            \"\"\n        );\n        imd.mint(bob, 1_000_000e18);\n    }\n\n    function _buy(uint256 imdIn) internal {\n        vm.startPrank(bob);\n        imd.approve(address(swapRouter), type(uint256).max);\n        bool zeroForOne = !gammaIs0;\n        swapRouter.swap(\n            key,\n            SwapParams({\n                zeroForOne: zeroForOne,\n                amountSpecified: -int256(imdIn),\n                sqrtPriceLimitX96: zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1\n            }),\n            PoolSwapTest.TestSettings({takeClaims: false, settleUsingBurn: false}),\n            \"\"\n        );\n        vm.stopPrank();\n    }\n\n    /// The 2215 configurator wires the game to a decoy hook over the REAL GAMMA (the real Reserve passes the balance\n    /// check). GammaHook has no way to see this: genesis() is now non-zero, so every fee-bearing swap on the launched\n    /// pool calls game.onHookFee, which reverts NotHook. The launch pool can never trade again.\n    function test_configuratorWiringToDecoyHookFreezesEveryFeeBearingSwap() public {\n        _buy(1_000e18); // trading works before the game is wired\n\n        DecoyHook decoy = new DecoyHook(IGammaGame(address(game)));\n        PoolKey memory fake = PoolKey({\n            currency0: key.currency0,\n            currency1: key.currency1,\n            fee: 3000, // any key that is not the launch pool's\n            tickSpacing: 60,\n            hooks: IHooks(address(decoy))\n        });\n        decoy.setPoolId(keccak256(abi.encode(fake)));\n        vm.prank(configurator);\n        game.initialize(fake);\n        assertEq(game.hook(), address(decoy));\n        assertEq(address(game.gamma()), address(gamma), \"the real GAMMA and real Reserve were accepted\");\n\n        vm.warp(block.timestamp + 2 hours);\n        // Expected: a buy on the launched GAMMA/IMD pool still pays its 3% fee to the game and succeeds.\n        // Actual: GammaHook._sendToGame -> GammaGame.onHookFee reverts NotHook, reverting the whole swap.\n        _buy(1_000e18);\n    }\n}","reproduction":"Deploy GammaHook(pm, imd, game) at mined flags, initialize the GAMMA/IMD pool (fee 12500, spacing 60), seed liquidity and send 800M GAMMA to the game. A 1,000 IMD exact-input buy succeeds pre-genesis. The configurator deploys a Decoy with game()=game and poolId()=keccak256(fakeKey), where fakeKey = {same GAMMA/IMD currencies, fee 3000, spacing 60, hooks: decoy}, and calls game.initialize(fakeKey), which succeeds. Warp 2h and buy 1,000 IMD exact input on the real pool. Expected: the swap fills and 3% of the output goes to the game. Actual: it reverts WrappedError(hook, afterSwap 0xb47b2fb1, NotHook 0xc72cfc38, HookCallFailed 0xa9e35b2f). I reran the attached proof with `forge test --match-path test/scratch/ConfiguratorBricksHook.t.sol`, and it fails on this code with exactly that error.","severity":"low","snippet":"        if (game.genesis() != 0) game.onHookFee(gamma, amount);","title":"Hook liveness depends on GammaGame accepting onHookFee: if the game is wired to any other hook, every fee-bearing swap on the launch pool reverts NotHook forever"},{"citation":"resolved","description":"Reported by audit_flow. On an exact-input sell, _collect calls poolManager.take(gamma, game, fee) inside afterSwap, which moves GAMMA out of the manager's ERC-20 balance. A router that calls sync(GAMMA), transfers its input and only then calls swap followed by settle() has its settle() credited for the transferred amount minus the hook's take. The seller's delta stays short by the fee, so the unlock reverts (CurrencyNotSettled). Standard routers (PoolSwapTest, V4Router SETTLE/SETTLE_ALL, Universal Router) settle after the swap and are unaffected. Buys are unaffected because the pre-paid currency is IMD. No funds are lost; it is an integration constraint that should be documented, or the fee could be minted as a claim instead.","line":166,"path":"src/GammaHook.sol","reproduction":"Pool after minute 96 (fee 300 bps). Router.unlockCallback: manager.sync(GAMMA) (reserves R = B); GAMMA.transfer(manager, 100_000e18) (balance B+100,000e18); manager.swap(key, {zeroForOne: gammaIs0, amountSpecified: -100_000e18, limit: MIN/MAX+-1}) -> afterSwap -> _collect -> take(gamma, game, 3_000e18) (balance B+97,000e18); manager.settle() credits 97,000e18 against a 100,000e18 debt; take(IMD output). Expected: the sell fills and 3,000e18 GAMMA goes to the game. Actual: the GAMMA delta is left at -3,000e18 and unlock reverts CurrencyNotSettled. The same router pre-paying 1,000e18 IMD for a buy succeeds. Traced against PoolManager.sync/_settle (paid = balanceNow - syncedReserves) and GammaHook lines 141-147 and 164-177.","severity":"low","snippet":"        if (gamma.balanceOf(address(poolManager)) >= amount) {","title":"Exact-input sells revert through routers that sync and pre-pay GAMMA before calling swap, because the hook takes the fee from the manager's balance mid-swap"},{"citation":"resolved","description":"Duplicate merged from audit_flow (info) and audit_economics (low): one root cause, v4 pools being permissionless. (1) Anyone can initialize PoolKey(GAMMA, IMD, any fee, any spacing, hooks=address(0)) on the same manager and trade there with no hook fee. (2) In the hooked pool, single-sided GAMMA liquidity just above the price is converted to IMD by buyers who pay only the buy-side fee. Neither path is a swap through this hook, and no hook code can close path 1. The brief's 'every swap' covers swaps in this pool. This is a design limitation to state in the README, not a code defect, so it is recorded as info.","line":69,"path":"src/GammaHook.sol","reproduction":"manager.initialize(PoolKey(c0, c1, 3000, 60, IHooks(address(0))), SQRT_PRICE_1_1); add 1,000,000e18 liquidity full range; sell 100,000e18 GAMMA exact-input on that key. Expected under a market-wide reading: 3,000e18 GAMMA to the Harvest. Actual: the game's balance and today's pot are unchanged, because the hook is never called for that key.","severity":"info","snippet":"            beforeAddLiquidity: false,","title":"The Harvest fee binds only to swaps in the hooked pool: a hookless GAMMA/IMD pool or range liquidity converts GAMMA to IMD without it"},{"citation":"resolved","description":"Reported by audit_economics. When the manager's GAMMA balance cannot cover a fee, _collect mints a claim and increments pendingFees. redeemFees() later sends the GAMMA to the game, and onHookFee funds _days[today()] at redemption time. Redemption is permissionless, so anyone can deliver it at once, and the amount can never be redirected. Only the day attribution depends on timing. This is documented behaviour; a keeper or the frontend should call redeemFees() whenever pendingFees() > 0.","line":185,"path":"src/GammaHook.sol","reproduction":"Drain the manager's GAMMA by buying with 1e29 IMD (balance under 1,200,000e18), then sell 40,000,000e18 GAMMA exact-input at 300 bps. pendingFees == 1,200,000e18 and today's pot is unchanged. Warp 1 day and call redeemFees(): the new day's pot rises by 1,200,000e18 and the swap's day received nothing (existing test test_deferredFeeCreditsHarvestWhenDelivered in test/GammaHookAudit.t.sol).","severity":"info","snippet":"    function redeemFees() external returns (uint256 amount) {","title":"A deferred sell fee is credited to the Harvest of the day it is redeemed, not the day of the swap"}],"hash":"4a6bad216708a1c3798ce0e942326b39dfb99e9d36b6fc10adccc9b14386c7d0","nodeId":"127a9089-57b8-42c8-b67d-8e7d542a8400","outcome":"completed","summary":"I found no critical, high or medium defects in `src/GammaHook.sol`, and nothing that stops the launch from deploying. I kept four findings: two low, two informational. They are in `.imd-findings.json` with a coverage row for all 18 entry points. I changed no repository files; the scratch test copy is deleted.\n\n**Findings**\n\n1. **Low – a wrongly wired game can stop all trading for good** (`src/GammaHook.sol:180`, `game.onHookFee(gamma, amount)`). Once the game has a start time, the hook reports every fee to it and does not handle a refusal. Only the game's configurator can cause this. If they set the game up with a decoy hook over the real GAMMA, the game refuses the call and every swap that pays a fee reverts, permanently. I ran the permissions specialist's proof test and it fails on the current code with exactly that error, so it is attached as the proof. I lowered it from medium because only that trusted configurator can trigger it, deliberately; a mistyped pool key is already rejected. The fix keeps the design: make the report best-effort with `try … catch`, since the game's `sweep()` already credits any GAMMA it holds but hasn't counted.\n2. **Low – sells fail through routers that pay before swapping** (`src/GammaHook.sol:166`). The hook takes the sell fee out of the pool manager's GAMMA in the middle of the swap. A router that pays its GAMMA into the manager before the swap is then credited for the fee less than it owes, and the whole transaction reverts. I traced this through the pool manager's accounting by hand and did not run a test. The standard Uniswap routers pay after the swap and are not affected, and no funds are lost.\n3. **Info – the fee can be avoided outside this pool** (`src/GammaHook.sol:69`). Two specialists reported this as the same root cause, so it is one finding here. Anyone can open a second GAMMA/IMD pool with no hook and trade there fee-free. Holders can also convert GAMMA to IMD by providing liquidity in this pool. Uniswap v4 pools are permissionless, so this is a design limit worth stating in the README, not a code defect.\n4. **Info – a delayed fee counts toward the day it is collected** (`src/GammaHook.sol:185`). When the manager can't pay a sell fee straight away, the fee is held until someone calls `redeemFees()`. It is then added to that day's Harvest, not the day of the swap. Anyone can call it at once and the amount can't be redirected. This is already documented, and an existing test shows it.\n\n**Coverage**\n- **Traced and correct:**\n  - all four GammaHook entry points;\n  - the six GammaGame entry points that touch the hook;\n  - all four token functions;\n  - the 99%→3% fee schedule;\n  - the rule that only the game may make exact-output buys.\n- **Not traced (marked `unreached`):** `DrandBeacon.prove`, `GammaGame.claim`, `GammaGame.claimMatured` and `GammaGame.settle`. These belong to the game, which is already deployed. Whether the game at `0xb333…` exists and has not yet been set up on the launch chain is chain state I could not check.\n\nThe test-writer and the math specialist reported no findings, and nothing from the static-analysis tools turned into a finding.","treeHash":null,"usage":{"cachedInputTokens":720617,"inputTokens":20,"model":"claude-opus-5-5","outputTokens":16597,"runtime":"claude","turns":11,"wallClockMs":198744}},{"artifacts":[],"attempt":1,"bundleHash":"b4cf928372dd27aff9710d43865fecbd77b50926a0e0b44f942663f9ba014fae","device":"d7953fb8f7e2c68b","findings":[],"hash":"96fa003a918c72e7f351588aafcaa7d288e9ddab8b9b7f03d8de9421e9903360","nodeId":"a1ed502a-00d0-4fdf-94a8-b07923cf36ae","outcome":"completed","summary":"Adapted GAMMA for the factory and fixed all four reproduced audit findings.\n\n`forge build` passes. `forge test` passes **163 tests** with the supplied fuzz seed.\n\nPartial exact-input sells now revert; deferred fees fund Harvest when redeemed. Changes and verification limits are documented in [ADAPTATION.md](/home/imd/.identitymd/work/3512e06f-1cd8-41ea-84f6-8e31b7bb985e/a1ed502a-00d0-4fdf-94a8-b07923cf36ae/ADAPTATION.md).","treeHash":"c49e7e2a4ef5c9464739fff2d2b70fdacbe38edb","usage":{"cachedInputTokens":4228352,"inputTokens":175164,"model":"gpt-6-astra","outputTokens":44384,"runtime":"codex","turns":10,"wallClockMs":1614164}},{"artifacts":[],"attempt":1,"bundleHash":"71b4a1c3e943c7b1834f89777bfdded76e027e8b2436c7f01e06d10497867764","device":"8ad841cd6518a96e","findings":[],"hash":"ab035bc984b8c94ec4d88e101425fd8c06b8de4761f9d13e32293b6b39e3dd07","nodeId":"a55c6e2e-1e0e-4327-9830-bea9ef65321b","outcome":"completed","summary":"Created [launch.json](/home/imd/.identitymd/work/3512e06f-1cd8-41ea-84f6-8e31b7bb985e/a55c6e2e-1e0e-4327-9830-bea9ef65321b/launch.json) with matching contracts, constructor arguments, six permissions, and required pool settings.\n\nManifest checks and `forge build` passed. All 163 tests passed with the specified fuzz seed. Only `launch.json` changed.","treeHash":"7c24fcd2512dcc15e57167daa1e895df5c8c88b6","usage":{"cachedInputTokens":2196864,"inputTokens":56454,"model":"gpt-6-astra","outputTokens":10122,"runtime":"codex","turns":14,"wallClockMs":824227}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"6d0f746f68a99ddf","findings":[],"hash":"be3e23f96d79e56f887594f70cf9e9313193f57bc9b26d14dfc1509c4ec80b6a","nodeId":"8980791f-0d44-4ea6-8191-d18ad97ba542","outcome":"failed","summary":"submodules did not fetch: Submodule 'contracts/lib/forge-std' (https://github.com/foundry-rs/forge-std) registered for path 'lib/forge-std'\nSubmodule 'contracts/lib/v4-core' (https://github.com/Uniswap/v4-core) registered for path 'lib/v4-core'\nCloning into 'E:/identitymd/work/3512e06f-1cd8-41ea-84f6-8e31b7bb985e/8980791f-0d44-4ea6-8191-d18ad97ba542/lib/forge-std'...\nCloning into 'E:/identitymd/work/3512e06f-1cd8-41ea-84f6","treeHash":null,"usage":{"cachedInputTokens":0,"inputTokens":0,"model":null,"outputTokens":0,"runtime":"claude","turns":0,"wallClockMs":30259}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"ee2cc12e7dfe571a","findings":[{"citation":"resolved","description":"On an exact-input sell the hook collects the reserved GAMMA fee inside afterSwap by calling poolManager.take(gamma, game, fee) (line 177) whenever the manager's GAMMA balance covers it. The seller has not settled yet, so the take is funded from the manager's existing GAMMA. If the seller's router follows the 'pre-fund' pattern (poolManager.sync(GAMMA); transfer GAMMA to the manager; swap; settle), the manager's GAMMA balance after the hook's take is below the reserves recorded by sync, and PoolManager._settle computes reservesNow - reservesBefore, which underflows and reverts. Buys through the same router work, because the hook takes GAMMA while the router pre-funded IMD. Routers that settle after the swap (PoolSwapTest, V4Router SETTLE/SETTLE_ALL, Universal Router) are unaffected. No funds are lost; the affected integration shape simply cannot sell through this pool. Documenting the constraint (settle the input only after the swap returns, or sync only right before settle) or collecting via the claim path when a GAMMA sync is pending would remove the trap. Verified with test/scratch/Probe.t.sol test_prefundRouterSellReverts (reverts) and test_prefundRouterBuyWorks (passes).","line":166,"path":"src/GammaHook.sol","reproduction":"State: fixture pool after the 99% window (fee 3%), alice holds 10M GAMMA. Router R.unlockCallback does: manager.sync(GAMMA); GAMMA.transferFrom(alice, manager, 100_000e18); manager.swap(key, {zeroForOne: gammaIs0, amountSpecified: -100_000e18, limit: MIN/MAX}); manager.settle(); take output. Expected: sell of 100,000 GAMMA fills, 3,000 GAMMA fee to the game. Actual: afterSwap -> _collect -> poolManager.take(gamma, game, 3_000e18) lowers the manager's GAMMA below the synced reserves (balance at sync + 100_000e18), settle() reverts with arithmetic underflow in _settle, whole sell reverts. The same router buying with 1,000 IMD pre-funded succeeds.","severity":"low","snippet":"        if (gamma.balanceOf(address(poolManager)) >= amount) {\n            _sendToGame(amount);","title":"Exact-input sells revert for routers that pre-fund the GAMMA input (sync+transfer) before calling swap, because the hook takes the fee from the manager mid-swap"},{"citation":"resolved","description":"Uniswap v4 pools are permissionless and the hook is only consulted for the one PoolKey that names it. Nothing in GammaToken or GammaHook prevents a second GAMMA/IMD pool with hooks = address(0) (or any other hook/fee tier). The 10% of supply distributed by the launch (NFT seats, agents) and any GAMMA bought through the hook pool can be used to seed such a pool, after which all trading can route around the 99%->3% fee and the Harvest receives nothing from it. This is inherent to the design (every IMD hook launch shares it) and the requester's brief treats the hook fee as the funding source of the Harvest, so it is recorded as a design limitation, not a code defect. Verified with test/scratch/Probe.t.sol test_hooklessPoolBypassesFee.","line":98,"path":"src/GammaHook.sol","reproduction":"State: fixture after setUp. Call manager.initialize(PoolKey(currency0, currency1, 3000, 60, IHooks(address(0))), SQRT_PRICE_1_1), add 1,000,000e18 liquidity full range, then alice sells 100,000 GAMMA exact-input on that key via PoolSwapTest. Expected under the brief: 3,000 GAMMA fee to the game's Harvest. Actual: pot unchanged, game GAMMA balance unchanged, alice paid exactly 100,000 GAMMA with no hook fee.","severity":"info","snippet":"        if (launchedAt != 0) revert PoolAlreadySet();","title":"The fee only governs the hook's own pool: anyone can open a hookless GAMMA/IMD pool on the same PoolManager and trade there with no Harvest fee"}],"hash":"d19b709269958b9ce96406d31db184fad023410d193808251fc725ae4570d523","nodeId":"080fb4a4-cb7a-4c22-a582-54f2c65b195b","outcome":"completed","summary":"The review is complete and `.imd-findings.json` holds the result. Git reports no tracked changes: only the findings file and the scratch test, both ignored, were created.\n\n**Outcome: no defect in the hook's control flow that loses funds or misroutes a fee.** Two behaviours are recorded, one low and one informational.\n\n- **Low, `src/GammaHook.sol` line 166.** The hook collects an exact-input sell's fee by taking GAMMA from the PoolManager inside `afterSwap`, before the seller has settled. A router that pre-funds its input by calling sync and transferring GAMMA before the swap then fails at settle, because the manager's balance has dropped below the synced reserves. The same router buying with IMD works. Standard routers settle after the swap and are unaffected. No funds are lost.\n- **Info, line 98.** The fee governs only the hook's own pool. Anyone can open a hookless GAMMA/IMD pool on the same manager and trade there with no fee reaching the Harvest. This is inherent to v4 and to every IMD hook launch, so it is recorded as a design limitation.\n\nBoth were reproduced with a scratch Foundry test under `test/scratch/`, which is not kept.\n\n**What was traced and holds.** All four swap shapes were checked against v4's `Hooks.afterSwap` sign and currency mapping and the manager's delta accounting. The take or claim-mint always nets the returned hook delta to zero. The full-fill equality for exact-input sells is exact because a zero remaining amount makes the specified delta equal the post-reservation amount. The game exemption cannot be spoofed since the sender is the manager's caller. Deferred fees, partial redemption, pre-genesis delivery and the genesis-gated `onHookFee` call were followed end to end, including the game's receipt check and its reentrancy guard. The fee schedule, the one-pool binding and the pool id used by the game's initialize were confirmed.\n\n**Deployment state checked on chain.** The game at the constructor address on Robinhood Chain is uninitialized, with zero genesis and no hook set. Its pool manager and IMD match the hook's arguments, and its runtime bytecode equals this tree's build outside immutables.\n\n**Coverage.** All 18 listed entry points have rows, plus three invariant rows. `afterSwap` carries the low finding. The beacon's BLS verification and the game's settlement internals are outside this area and were only traced where a hook path touches them.","treeHash":null,"usage":{"cachedInputTokens":1683670,"inputTokens":354,"model":"claude-fable-5-1","outputTokens":46453,"runtime":"claude","turns":29,"wallClockMs":700806}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"cd8076510aad8b8e","findings":[],"hash":"ea0d558de8df1902582a5dce73b5e872bdfdbca18841934ff248d6e1c261d859","nodeId":"b0fe07b2-68c9-4b6c-afb2-480ca04582cc","outcome":"completed","summary":"The review is complete. `.imd-findings.json` holds an empty findings list and 22 coverage rows (all 18 listed entry points plus four invariants). The working tree is otherwise untouched; the scratch test was removed.\n\n**Outcome: no defect found in the assigned area.** I checked src/GammaHook.sol against the Math Precision, Boundary and Numerical Gap guides and could not produce a failing input.\n\n**What I traced and confirmed**\n\n- **Fee schedule.** The decay is floor-minutes times 100 bps from the later of pool creation and genesis, held at 9900 before genesis. Minute 95 gives 400, minute 96 gives 300, forever. Genesis can never precede pool creation because the game's initialize requires the hook's poolId, so the subtraction cannot underflow.\n- **Three fee shapes, both currency orders.** Exact-input buys take floor(output × rate / 10000) from the GAMMA output. Exact-output sells gross up by rate / (10000 − rate), so the Harvest gets exactly rate/10000 of the seller's total payment. The denominator is at least 100. Exact-input sells reserve the fee in beforeSwap and the afterSwap full-fill check recomputes the same value in the same block. Delta sign and currency placement match the vendored Hooks.afterSwap for GAMMA as currency0 and as currency1.\n- **Boundaries.** A 1-wei sell at 99% reserves 0 and sends 1 wei to the pool, so the swap amount is never zero and HookDeltaExceedsSwapAmount is unreachable. Fees above int128 revert through SafeCast rather than wrap. A buy fee is always coverable by the manager's balance. A sell fee larger than that balance is minted as a claim backed by the seller's own settlement and redeemed later to the fixed recipient.\n- **Deployed dependency.** Every swap calls the hardcoded game's genesis(), so I checked chain 4663: the game at 0xb333a22c… is byte-identical to src/GammaGame.sol after masking immutables, its IMD and PoolManager immutables match the constructor spec, genesis is still 0, and the ADAPTATION's IMD-fee text is retained history that section 10 already closes.\n\n**Evidence run**\n\n| Suite | Result |\n|---|---|\n| Existing hook suites (6 files) | 48 passed |\n| Scratch probes: tiny sells 1–205 wei at 99%, exact-out sell gross-up fuzz, exact-in buy fee fuzz | 4 passed, 512 runs each fuzz |\n\n**Not reached:** DrandBeacon.prove, which is BLS verification outside this area and already deployed.","treeHash":null,"usage":{"cachedInputTokens":1616891,"inputTokens":386,"model":"claude-fable-5-1","outputTokens":40595,"runtime":"claude","turns":29,"wallClockMs":1002103}},{"artifacts":[],"attempt":1,"bundleHash":"90d3b9a99ce530868cb592fa1085efddbe94cc1fb8f00850a36283f5bc071732","device":"6a7dea7fd5378a40","findings":[],"hash":"fb450ad3667c5933aee71a05b573e38464de2ffdc735437f8e3198f6ce90bda4","nodeId":"bd7250a8-131f-43cb-baf1-95fc37f308c3","outcome":"completed","summary":"Added failure-path, boundary, fuzz, and stateful invariant tests under `test/`, including fee conservation, deferred claims, both token orderings, and GAMMA-only pool launches.\n\nValidation passed:\n- `forge build`\n- Seeded `forge test`: **177 passed, 0 failed**\n- **16,384** new randomized invariant calls\n\nNo production or configuration files changed. No new reproducible defects found. Integration testing ran offline with the real local v4 PoolManager.","treeHash":"3137063847bc54b7dd64fb1b1771f226f9437346","usage":{"cachedInputTokens":2161152,"inputTokens":130032,"model":"gpt-6-astra","outputTokens":21719,"runtime":"codex","turns":8,"wallClockMs":744602}}],"verification":[{"checks":[{"durationMs":182176,"exitCode":0,"name":"build","output":"Compiling 108 files with Solc 0.8.26\nSolc 0.8.26 finished in 182.06s\nCompiler run successful!\n","passed":true},{"durationMs":10987,"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 956.13µs (759.05µ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.32ms (114.62µs CPU time)\n\nRan 1 test for test/GammaRevision.t.sol:GammaRevisionFeeClockTest\n[PASS] test_feeHeldAtStartUntilGameGenesis() (gas: 215999)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.85ms (396.99µs CPU time)\n\nRan 1 test for test/GammaRevisionFailurePaths.t.sol:GammaRevisionFailurePathsTest\n[PASS] test_rejectedPurchaseIntoProvenEpochMovesNothingAndNextEpochOpens() (gas: 692643)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.02ms (381.12µs CPU time)\n\nRan 3 tests for test/GammaHookAudit.t.sol:GammaHookBeforeGenesisTest\n[PASS] test_preGenesisDeferredFeesCanBeRedeemedAfterInitialize() (gas: 1244652)\n[PASS] test_preGenesisDeferredFeesCanBeRedeemedBeforeInitialize() (gas: 1225210)\n[PASS] test_preGenesisFeesAreFirstHarvestSurplus() (gas: 1085086)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 4.13ms (2.14ms CPU time)\n\nRan 11 tests for test/GammaFailurePaths.t.sol:GammaFailurePathsTest\n[PASS] test_claimArrayWithForeignBatchRevertsEveryEarlierClaim() (gas: 1458613)\n[PASS] test_duplicateBatchIdsCannotMultiplyPayoutOrBonus() (gas: 1842557)\n[PASS] test_failedDonationDoesNotFundHarvest() (gas: 107425)\n[PASS] test_failedSlippageRollsBackAutoSettlementAndTransfer() (gas: 1535378)\n[PASS] test_initializeRejectsMissingImdOrHookBeforeFunding() (gas: 77435)\n[PASS] test_initializeRollbackAllowsCorrectKeyAfterMistake() (gas: 877817)\n[PASS] test_missingAllowanceAndBalanceCannotCreateFreeEnergies() (gas: 328932)\n[PASS] test_outOfRangeClaimRevertsAndEmptyClaimsAreNoOps() (gas: 1152296)\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 5.49ms (5.46ms 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 7.62ms (9.00ms CPU time)\n\nRan 2 tests for test/GammaHookLaunch.t.sol:GammaTokenLaunchTest\n[PASS] test_neitherFactoryNorHolderCanMintOrConfigureToken() (gas: 273415)\n[PASS] test_supplyMetadataAndTransfer() (gas: 108102)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 8.46ms (856.71µs CPU time)\n\nRan 3 tests for test/GammaHookLaunch.t.sol:GammaHookLaunchTest\n[PASS] test_factoryDeploysAllocatesInitializesAndCollects() (gas: 8735104)\n[PASS] test_hookConstructorNeedsNoLiveDependencies() (gas: 280618)\n[PASS] test_permissionsAreExactlyTheSixRequested() (gas: 9070)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 12.28ms (11.81ms 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 30.02ms (29.88ms CPU time)\n\nRan 10 tests for test/GammaHook.t.sol:GammaHookTest\n[PASS] test_buyExactInFeeInGamma() (gas: 374876)\n[PASS] test_exactOutBuyIsRejected() (gas: 163184)\n[PASS] test_feeSchedule() (gas: 126009)\n[PASS] test_gamePurchasesAreExempt() (gas: 631566)\n[PASS] test_onlyHookCanReportFees() (gas: 40264)\n[PASS] test_onlyPoolManager() (gas: 38799)\n[PASS] test_secondPoolRejected() (gas: 783537)\n[PASS] test_sellExactInFeeInGamma() (gas: 326891)\n[PASS] test_sniperFeeFundsFirstHarvest() (gas: 380408)\n[PASS] test_tradingFeesReachMiners() (gas: 1531679)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 172.11ms (2.96ms CPU time)\n\nRan 7 tests for test/DrandBeaconAdversarial.t.sol:DrandBeaconAdversarialTest\n[PASS] testFuzz_rejectsEveryNoncanonicalLength(uint8,uint64) (runs: 1000, μ: 37495, ~: 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 276.92ms (301.24ms CPU time)\n\nRan 9 tests for test/GammaEmission.t.sol:GammaEmissionTest\n[PASS] testFuzz_betweenAdds(uint32,uint32,uint32) (runs: 512, μ: 30802, ~: 29895)\n[PASS] testFuzz_monotonic(uint32,uint32) (runs: 512, μ: 14472, ~: 14066)\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 279.69ms (556.65ms CPU time)\n\nRan 10 tests for test/GammaLaunch.t.sol:GammaLaunchTest\n[PASS] test_backedGammaFeeIsCredited() (gas: 846713)\n[PASS] test_configuratorCannotDrainReserveThroughItsHook() (gas: 1033277)\n[PASS] test_heldGammaFollowsEveryFlow() (gas: 1701879)\n[PASS] test_hookFeesStillFlowWithTheRealHook() (gas: 373676)\n[PASS] test_imdFeeIsRejected() (gas: 766680)\n[PASS] test_initializeRecordsHeldGamma() (gas: 25870)\n[PASS] test_initializeRejectsHookBoundToAnotherGame() (gas: 378807)\n[PASS] test_initializeRejectsHookWithoutCode() (gas: 378453)\n[PASS] test_initializeRejectsKeyOfAnotherPool() (gas: 379108)\n[PASS] test_unbackedGammaFeeIsRejected() (gas: 765373)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 279.68ms (6.37ms CPU time)\n\nRan 13 tests for test/GammaSettlementProperties.t.sol:GammaSettlementPropertiesTest\n[PASS] testFuzz_claimFeeIsBoundedAndNeverIncreases(uint32,uint32) (runs: 1000, μ: 37558, ~: 37599)\n[PASS] testFuzz_drawRiskIsMonotonicAndExtraEnergyAddsAtMostOneSurvivor(bytes32,uint256,uint16,uint16) (runs: 512, μ: 307610, ~: 227231)\n[PASS] testFuzz_eachEpochAccountsForItsEntireTranche(uint16[4],bytes32) (runs: 256, μ: 2027123, ~: 2013848)\n[PASS] testFuzz_roundIsFirstAtOrAfterRequiredDelay(uint32) (runs: 1000, μ: 28666, ~: 28622)\n[PASS] testFuzz_settlementCadenceCannotChangeOutcome(uint16[4],bytes32) (runs: 128, μ: 3363526, ~: 3339774)\n[PASS] test_badProofDoesNotBlockPurchaseOrInventRandomness() (gas: 1316163)\n[PASS] test_batchPaginationHandlesEmptyPagesAndKeepsNewestFirst() (gas: 1020741)\n[PASS] test_futureHarvestDoesNotReceivePastClaimBonuses() (gas: 2629990)\n[PASS] test_guardResetsPerEpochAndLiftsAtExactBoundary() (gas: 3019247)\n[PASS] test_harvestAtExactDayBoundaryUsesItsOwnUnit() (gas: 1272171)\n[PASS] test_maturedClaimSkipsUntilTheExact24HourBoundary() (gas: 1352542)\n[PASS] test_oneUnitDrawsOneBatchAndFinalizationNeedsAnotherUnit() (gas: 1785624)\n[PASS] test_settlementWorkIsClampedTo256EvenForMaximumInput() (gas: 68778329)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 279.74ms (1.30s CPU time)\n\nRan 9 tests for test/GammaClaimBonus.t.sol:GammaClaimBonusTest\n[PASS] testFuzz_anySplitPaysTheSame(uint8,uint8) (runs: 512, μ: 3183658, ~: 3146748)\n[PASS] test_batchesHarvestedLaterAreNotPenalised() (gas: 3592584)\n[PASS] test_bonusFromOthersFeesIsStillPaid() (gas: 1980452)\n[PASS] test_loneClaimantSplitSendsTheThirdToTheHarvest() (gas: 1869051)\n[PASS] test_selfIndexCheckpointsOnlyOnFeeSharing() (gas: 1133810)\n[PASS] test_splitClaimPaysTheSameAsOneClaim() (gas: 2747127)\n[PASS] test_sweepCreditsStrayGammaToTheHarvest() (gas: 239265)\n[PASS] test_sweepNeedsInitialize() (gas: 44217)\n[PASS] test_sweptGammaIsPaidOutThroughTheHarvest() (gas: 1454496)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 278.41ms (288.54ms CPU time)\n\nRan 14 tests for test/GammaStateMachine.t.sol:GammaEdgeCasesTest\n[PASS] testFuzz_claimFeeEndpoints(uint256,uint256) (runs: 1000, μ: 20490, ~: 20598)\n[PASS] testFuzz_failRateIsClampedToAir(uint256) (runs: 1000, μ: 6024, ~: 6022)\n[PASS] testFuzz_survivorEdgesOfFailRate(bytes32,uint256,uint256) (runs: 1000, μ: 158920, ~: 161755)\n[PASS] test_claimMaturedBeforeHarvestPaysNothing() (gas: 832212)\n[PASS] test_claimedBatchCollectsNoLaterBonus() (gas: 1989028)\n[PASS] test_feeReportFromConfiguratorOrPoolManagerIsRejected() (gas: 110903)\n[PASS] test_hookCannotCreditTheSameTokensTwice() (gas: 156801)\n[PASS] test_machineAndAmountEdgesAreRejected() (gas: 621781)\n[PASS] test_noSettlementWithoutBeaconEvenAfterManyDays() (gas: 910591)\n[PASS] test_settleWithZeroUnitsMovesNothing() (gas: 603073)\n[PASS] test_strayTransferSweptOnceOnly() (gas: 153240)\n[PASS] test_sweepWithNothingStrayReturnsZeroAndChangesNothing() (gas: 91933)\n[PASS] test_zeroHookFeeIsANoOp() (gas: 111306)\n[PASS] test_zeroImdLimitCannotBuyAndKeepsNoTokens() (gas: 213401)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 279.79ms (421.12ms CPU time)\n\nRan 15 tests for test/GammaHookAudit.t.sol:GammaHookGamma0AuditTest\n[PASS] testFuzz_exactOutputSellPaysScheduledFraction(uint96,uint16) (runs: 512, μ: 462278, ~: 462344)\n[PASS] testFuzz_multipleFeesRemainFullyBacked(uint96,uint8,bool) (runs: 512, μ: 2005177, ~: 1922787)\n[PASS] test_allEnabledCallbacksAndRedemptionRefuseForgedCallers() (gas: 128373)\n[PASS] test_deferredFeeCreditsHarvestWhenDelivered() (gas: 1007880)\n[PASS] test_exactInputSellWithReachableLimitStillWorks() (gas: 395856)\n[PASS] test_exactOutputPartialSellChargesOnlyRealisedInput() (gas: 427709)\n[PASS] test_exactOutputSellPaysSameLaunchFeeAsExactInput() (gas: 785894)\n[PASS] test_feeDeltasRejectOverflowInsteadOfWrapping() (gas: 95995)\n[PASS] test_gameIsExemptForAllFourSwapShapes() (gas: 216786)\n[PASS] test_hookDataCannotSpoofGameExemption() (gas: 140004)\n[PASS] test_largeExactOutputSellAlsoDefersFee() (gas: 969333)\n[PASS] test_largeSellDefersThenRedeemsEntireFee() (gas: 1124382)\n[PASS] test_partialFillSellRevertsAtomically() (gas: 359011)\n[PASS] test_redemptionCanPayPartThenFinish() (gas: 1222689)\n[PASS] test_smallestSellRoundsFeeDownWithoutReportingIt() (gas: 351913)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 277.76ms (489.56ms CPU time)\n\nRan 2 tests for test/GammaRealBeacon.t.sol:GammaRealBeaconTest\n[PASS] test_invalidRealProofIsIgnoredWithoutDrawingBatch() (gas: 627365)\n[PASS] test_realProofUnblocksSettlementAndMaturedClaim() (gas: 1087737)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 297.27ms (5.70ms 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, μ: 63447, ~: 54158)\n[PASS] test_beaconProofsAttachedToPurchase() (gas: 1069350)\n[PASS] test_buyBounds() (gas: 108798)\n[PASS] test_buyBurnsGammaAndRecordsBatch() (gas: 519586)\n[PASS] test_buyRefundsUnusedImd() (gas: 466197)\n[PASS] test_buySlippage() (gas: 211110)\n[PASS] test_claimBeforeHarvestPaysNothing() (gas: 498520)\n[PASS] test_claimFeeSchedule() (gas: 82819)\n[PASS] test_claimMaturedSkipsYoungDays() (gas: 1226876)\n[PASS] test_claimNotOwnerReverts() (gas: 1044480)\n[PASS] test_claimPaysWinningsMinusFee() (gas: 1393314)\n[PASS] test_conservation() (gas: 4336831)\n[PASS] test_dealIsFair() (gas: 35263063)\n[PASS] test_earlyClaimersPayPatientOnes() (gas: 1997485)\n[PASS] test_emptyDayRollsIntoNextHarvest() (gas: 303257)\n[PASS] test_epochWaitsForBeacon() (gas: 913448)\n[PASS] test_gasNormalPurchase() (gas: 722940)\nLogs:\n  purchase gas (same epoch): 290916\n\n[PASS] test_gasPurchaseDoingMaxSettlement() (gas: 3979780)\nLogs:\n  purchase gas (8 large batches settled): 1687651\n\n[PASS] test_guardCapsFirstWeek() (gas: 1494594)\n[PASS] test_guardLiftsAfterAWeek() (gas: 2017168)\n[PASS] test_initializeNeedsReserve() (gas: 161034)\n[PASS] test_initializeOnlyConfigurator() (gas: 44139)\n[PASS] test_initializeOnlyOnce() (gas: 40968)\n[PASS] test_noFallbackSeedOnlyDrand() (gas: 2819184)\n[PASS] test_roundForEpochIsAfterClose() (gas: 49061)\n[PASS] test_survivalRatesMatchMachines() (gas: 24427121)\n[PASS] test_survivorsSplitShares_failsSplitHarvest() (gas: 1437484)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 297.47ms (613.42ms CPU time)\n\nRan 15 tests for test/GammaHookAudit.t.sol:GammaHookGamma1AuditTest\n[PASS] testFuzz_exactOutputSellPaysScheduledFraction(uint96,uint16) (runs: 512, μ: 457943, ~: 457926)\n[PASS] testFuzz_multipleFeesRemainFullyBacked(uint96,uint8,bool) (runs: 512, μ: 2005958, ~: 1926181)\n[PASS] test_allEnabledCallbacksAndRedemptionRefuseForgedCallers() (gas: 128098)\n[PASS] test_deferredFeeCreditsHarvestWhenDelivered() (gas: 1012086)\n[PASS] test_exactInputSellWithReachableLimitStillWorks() (gas: 389945)\n[PASS] test_exactOutputPartialSellChargesOnlyRealisedInput() (gas: 422505)\n[PASS] test_exactOutputSellPaysSameLaunchFeeAsExactInput() (gas: 775793)\n[PASS] test_feeDeltasRejectOverflowInsteadOfWrapping() (gas: 95974)\n[PASS] test_gameIsExemptForAllFourSwapShapes() (gas: 216646)\n[PASS] test_hookDataCannotSpoofGameExemption() (gas: 139595)\n[PASS] test_largeExactOutputSellAlsoDefersFee() (gas: 973776)\n[PASS] test_largeSellDefersThenRedeemsEntireFee() (gas: 1128588)\n[PASS] test_partialFillSellRevertsAtomically() (gas: 353603)\n[PASS] test_redemptionCanPayPartThenFinish() (gas: 1226895)\n[PASS] test_smallestSellRoundsFeeDownWithoutReportingIt() (gas: 343846)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 288.77ms (471.71ms 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           | 360   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | buy               | 307   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | buyIntoKnownEpoch | 346   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | claimExact        | 358   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | claimLagging      | 353   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | donate            | 348   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | foreignClaim      | 315   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | settle            | 354   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | sweep             | 331   | 0       | 0        |\n╰--------------------------+-------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.19s (1.18s 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      | 799   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| GammaHandler | claim    | 802   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| GammaHandler | proveAll | 837   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| GammaHandler | trade    | 814   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| GammaHandler | warp     | 844   | 0       | 0        |\n╰--------------+----------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.20s (1.20s 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            | 1755  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | buy                | 1680  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | claim              | 1831  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | donate             | 1758  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | rejectForeignClaim | 1699  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | settle             | 1820  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | trade              | 1745  | 0       | 0        |\n╰------------------------+--------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 10.85s (10.85s CPU time)\n\nRan 24 test suites in 10.86s (16.33s CPU time): 163 tests passed, 0 failed, 0 skipped (163 total tests)\n","passed":true},{"durationMs":122,"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.redeemFees()\",\"GammaHook.unlockCallback(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\":457,\"PROJECT.md\":180,\"README.md\":136,\"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/00-audit.json\":6,\"launch/01-game.json\":23,\"launch/02-hook.json\":28,\"launch/2215-game-manifest.json\":19,\"launch/pay.sh\":45,\"script/DeployLocal.s.sol\":93,\"script/HookMiner.sol\":23,\"script/InitializeGame.s.sol\":36,\"src/DrandBeacon.sol\":58,\"src/GammaGame.sol\":741,\"src/GammaHook.sol\":251,\"src/GammaToken.sol\":14,\"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/GammaHookAudit.t.sol\":389,\"test/GammaHookLaunch.t.sol\":202,\"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\":206},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":8607,"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:85: GammaHook.feeBps() (src/GammaHook.sol#85-93) 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/GammaHook.sol:130: GammaHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/GammaHook.sol#130-158) 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/GammaHook.sol:85: GammaHook.feeBps() (src/GammaHook.sol#85-93) 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/GammaHook.sol:185: GammaHook.redeemFees() (src/GammaHook.sol#185-188) 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/GammaGame.sol:643: GammaGame.pendingRound() (src/GammaGame.sol#643-649) 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:53: GammaHook.constructor(IPoolManager,address,IGammaGame).imd_ (src/GammaHook.sol#53) 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:191: Reentrancy in GammaHook.unlockCallback(bytes) (src/GammaHook.sol#191-202):\n[low/medium] reentrancy-events at src/GammaHook.sol:164: Reentrancy in GammaHook._collect(uint256,uint256) (src/GammaHook.sol#164-174):\n[low/medium] reentrancy-events at src/GammaHook.sol:164: Reentrancy in GammaHook._collect(uint256,uint256) (src/GammaHook.sol#164-174):\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:97: GammaHook.beforeInitialize(address,PoolKey,uint160) (src/GammaHook.sol#97-101) 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/GammaHook.sol:130: GammaHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/GammaHook.sol#130-158) uses timestamp for comparisons\n[low/medium] timestamp at src/GammaHook.sol:164: GammaHook._collect(uint256,uint256) (src/GammaHook.sol#164-174) 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/GammaHook.sol:85: GammaHook.feeBps() (src/GammaHook.sol#85-93) 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:185: GammaHook.redeemFees() (src/GammaHook.sol#185-188) 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/GammaGame.sol:186: GammaGame.initialize(PoolKey) (src/GammaGame.sol#186-213) uses timestamp for comparisons","passed":true},{"durationMs":793,"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:90: 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] 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:103: 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":"96fa003a918c72e7f351588aafcaa7d288e9ddab8b9b7f03d8de9421e9903360","verifiedTreeHash":"c49e7e2a4ef5c9464739fff2d2b70fdacbe38edb","verifierVersion":"0.1.0+a6ee6548"},{"checks":[{"durationMs":159005,"exitCode":0,"name":"build","output":"Compiling 108 files with Solc 0.8.26\nSolc 0.8.26 finished in 158.92s\nCompiler run successful!\n","passed":true},{"durationMs":10186,"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 664.21µs (518.51µ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.70ms (99.17µs CPU time)\n\nRan 1 test for test/GammaRevision.t.sol:GammaRevisionFeeClockTest\n[PASS] test_feeHeldAtStartUntilGameGenesis() (gas: 215999)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.86ms (322.23µs CPU time)\n\nRan 1 test for test/GammaRevisionFailurePaths.t.sol:GammaRevisionFailurePathsTest\n[PASS] test_rejectedPurchaseIntoProvenEpochMovesNothingAndNextEpochOpens() (gas: 692643)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.50ms (446.38µs CPU time)\n\nRan 10 tests for test/GammaHook.t.sol:GammaHookTest\n[PASS] test_buyExactInFeeInGamma() (gas: 374876)\n[PASS] test_exactOutBuyIsRejected() (gas: 163184)\n[PASS] test_feeSchedule() (gas: 126009)\n[PASS] test_gamePurchasesAreExempt() (gas: 631566)\n[PASS] test_onlyHookCanReportFees() (gas: 40264)\n[PASS] test_onlyPoolManager() (gas: 38799)\n[PASS] test_secondPoolRejected() (gas: 783537)\n[PASS] test_sellExactInFeeInGamma() (gas: 326891)\n[PASS] test_sniperFeeFundsFirstHarvest() (gas: 380408)\n[PASS] test_tradingFeesReachMiners() (gas: 1531679)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 3.97ms (2.31ms CPU time)\n\nRan 10 tests for test/GammaLaunch.t.sol:GammaLaunchTest\n[PASS] test_backedGammaFeeIsCredited() (gas: 846713)\n[PASS] test_configuratorCannotDrainReserveThroughItsHook() (gas: 1033277)\n[PASS] test_heldGammaFollowsEveryFlow() (gas: 1701879)\n[PASS] test_hookFeesStillFlowWithTheRealHook() (gas: 373676)\n[PASS] test_imdFeeIsRejected() (gas: 766680)\n[PASS] test_initializeRecordsHeldGamma() (gas: 25870)\n[PASS] test_initializeRejectsHookBoundToAnotherGame() (gas: 378807)\n[PASS] test_initializeRejectsHookWithoutCode() (gas: 378453)\n[PASS] test_initializeRejectsKeyOfAnotherPool() (gas: 379108)\n[PASS] test_unbackedGammaFeeIsRejected() (gas: 765373)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 6.01ms (4.94ms CPU time)\n\nRan 2 tests for test/GammaHookLaunch.t.sol:GammaTokenLaunchTest\n[PASS] test_neitherFactoryNorHolderCanMintOrConfigureToken() (gas: 273415)\n[PASS] test_supplyMetadataAndTransfer() (gas: 108102)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 6.85ms (856.28µs CPU time)\n\nRan 2 tests for test/GammaRealBeacon.t.sol:GammaRealBeaconTest\n[PASS] test_invalidRealProofIsIgnoredWithoutDrawingBatch() (gas: 627365)\n[PASS] test_realProofUnblocksSettlementAndMaturedClaim() (gas: 1087737)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 7.02ms (4.42ms CPU time)\n\nRan 3 tests for test/GammaHookAudit.t.sol:GammaHookBeforeGenesisTest\n[PASS] test_preGenesisDeferredFeesCanBeRedeemedAfterInitialize() (gas: 1244652)\n[PASS] test_preGenesisDeferredFeesCanBeRedeemedBeforeInitialize() (gas: 1225210)\n[PASS] test_preGenesisFeesAreFirstHarvestSurplus() (gas: 1085086)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 13.09ms (10.89ms CPU time)\n\nRan 11 tests for test/GammaFailurePaths.t.sol:GammaFailurePathsTest\n[PASS] test_claimArrayWithForeignBatchRevertsEveryEarlierClaim() (gas: 1458613)\n[PASS] test_duplicateBatchIdsCannotMultiplyPayoutOrBonus() (gas: 1842557)\n[PASS] test_failedDonationDoesNotFundHarvest() (gas: 107425)\n[PASS] test_failedSlippageRollsBackAutoSettlementAndTransfer() (gas: 1535378)\n[PASS] test_initializeRejectsMissingImdOrHookBeforeFunding() (gas: 77435)\n[PASS] test_initializeRollbackAllowsCorrectKeyAfterMistake() (gas: 877817)\n[PASS] test_missingAllowanceAndBalanceCannotCreateFreeEnergies() (gas: 328932)\n[PASS] test_outOfRangeClaimRevertsAndEmptyClaimsAreNoOps() (gas: 1152296)\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 8.56ms (5.84ms 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 15.68ms (15.54ms CPU time)\n\nRan 3 tests for test/GammaHookLaunch.t.sol:GammaHookLaunchTest\n[PASS] test_factoryDeploysAllocatesInitializesAndCollects() (gas: 8735104)\n[PASS] test_hookConstructorNeedsNoLiveDependencies() (gas: 280618)\n[PASS] test_permissionsAreExactlyTheSixRequested() (gas: 9070)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 16.93ms (17.97ms 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, μ: 63447, ~: 54158)\n[PASS] test_beaconProofsAttachedToPurchase() (gas: 1069350)\n[PASS] test_buyBounds() (gas: 108798)\n[PASS] test_buyBurnsGammaAndRecordsBatch() (gas: 519586)\n[PASS] test_buyRefundsUnusedImd() (gas: 466197)\n[PASS] test_buySlippage() (gas: 211110)\n[PASS] test_claimBeforeHarvestPaysNothing() (gas: 498520)\n[PASS] test_claimFeeSchedule() (gas: 82819)\n[PASS] test_claimMaturedSkipsYoungDays() (gas: 1226876)\n[PASS] test_claimNotOwnerReverts() (gas: 1044480)\n[PASS] test_claimPaysWinningsMinusFee() (gas: 1393314)\n[PASS] test_conservation() (gas: 4336831)\n[PASS] test_dealIsFair() (gas: 35263063)\n[PASS] test_earlyClaimersPayPatientOnes() (gas: 1997485)\n[PASS] test_emptyDayRollsIntoNextHarvest() (gas: 303257)\n[PASS] test_epochWaitsForBeacon() (gas: 913448)\n[PASS] test_gasNormalPurchase() (gas: 722940)\nLogs:\n  purchase gas (same epoch): 290916\n\n[PASS] test_gasPurchaseDoingMaxSettlement() (gas: 3979780)\nLogs:\n  purchase gas (8 large batches settled): 1687651\n\n[PASS] test_guardCapsFirstWeek() (gas: 1494594)\n[PASS] test_guardLiftsAfterAWeek() (gas: 2017168)\n[PASS] test_initializeNeedsReserve() (gas: 161034)\n[PASS] test_initializeOnlyConfigurator() (gas: 44139)\n[PASS] test_initializeOnlyOnce() (gas: 40968)\n[PASS] test_noFallbackSeedOnlyDrand() (gas: 2819184)\n[PASS] test_roundForEpochIsAfterClose() (gas: 49061)\n[PASS] test_survivalRatesMatchMachines() (gas: 24427121)\n[PASS] test_survivorsSplitShares_failsSplitHarvest() (gas: 1437484)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 233.54ms (334.81ms CPU time)\n\nRan 14 tests for test/GammaStateMachine.t.sol:GammaEdgeCasesTest\n[PASS] testFuzz_claimFeeEndpoints(uint256,uint256) (runs: 1000, μ: 20490, ~: 20598)\n[PASS] testFuzz_failRateIsClampedToAir(uint256) (runs: 1000, μ: 6024, ~: 6022)\n[PASS] testFuzz_survivorEdgesOfFailRate(bytes32,uint256,uint256) (runs: 1000, μ: 158920, ~: 161755)\n[PASS] test_claimMaturedBeforeHarvestPaysNothing() (gas: 832212)\n[PASS] test_claimedBatchCollectsNoLaterBonus() (gas: 1989028)\n[PASS] test_feeReportFromConfiguratorOrPoolManagerIsRejected() (gas: 110903)\n[PASS] test_hookCannotCreditTheSameTokensTwice() (gas: 156801)\n[PASS] test_machineAndAmountEdgesAreRejected() (gas: 621781)\n[PASS] test_noSettlementWithoutBeaconEvenAfterManyDays() (gas: 910591)\n[PASS] test_settleWithZeroUnitsMovesNothing() (gas: 603073)\n[PASS] test_strayTransferSweptOnceOnly() (gas: 153240)\n[PASS] test_sweepWithNothingStrayReturnsZeroAndChangesNothing() (gas: 91933)\n[PASS] test_zeroHookFeeIsANoOp() (gas: 111306)\n[PASS] test_zeroImdLimitCannotBuyAndKeepsNoTokens() (gas: 213401)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 250.89ms (258.22ms CPU time)\n\nRan 9 tests for test/GammaEmission.t.sol:GammaEmissionTest\n[PASS] testFuzz_betweenAdds(uint32,uint32,uint32) (runs: 512, μ: 30802, ~: 29895)\n[PASS] testFuzz_monotonic(uint32,uint32) (runs: 512, μ: 14472, ~: 14066)\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 260.91ms (260.79ms 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 264.48ms (24.33ms CPU time)\n\nRan 15 tests for test/GammaHookAudit.t.sol:GammaHookGamma0AuditTest\n[PASS] testFuzz_exactOutputSellPaysScheduledFraction(uint96,uint16) (runs: 512, μ: 462278, ~: 462344)\n[PASS] testFuzz_multipleFeesRemainFullyBacked(uint96,uint8,bool) (runs: 512, μ: 2005177, ~: 1922787)\n[PASS] test_allEnabledCallbacksAndRedemptionRefuseForgedCallers() (gas: 128373)\n[PASS] test_deferredFeeCreditsHarvestWhenDelivered() (gas: 1007880)\n[PASS] test_exactInputSellWithReachableLimitStillWorks() (gas: 395856)\n[PASS] test_exactOutputPartialSellChargesOnlyRealisedInput() (gas: 427709)\n[PASS] test_exactOutputSellPaysSameLaunchFeeAsExactInput() (gas: 785894)\n[PASS] test_feeDeltasRejectOverflowInsteadOfWrapping() (gas: 95995)\n[PASS] test_gameIsExemptForAllFourSwapShapes() (gas: 216786)\n[PASS] test_hookDataCannotSpoofGameExemption() (gas: 140004)\n[PASS] test_largeExactOutputSellAlsoDefersFee() (gas: 969333)\n[PASS] test_largeSellDefersThenRedeemsEntireFee() (gas: 1124382)\n[PASS] test_partialFillSellRevertsAtomically() (gas: 359011)\n[PASS] test_redemptionCanPayPartThenFinish() (gas: 1222689)\n[PASS] test_smallestSellRoundsFeeDownWithoutReportingIt() (gas: 351913)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 264.52ms (476.38ms CPU time)\n\nRan 15 tests for test/GammaHookAudit.t.sol:GammaHookGamma1AuditTest\n[PASS] testFuzz_exactOutputSellPaysScheduledFraction(uint96,uint16) (runs: 512, μ: 457943, ~: 457926)\n[PASS] testFuzz_multipleFeesRemainFullyBacked(uint96,uint8,bool) (runs: 512, μ: 2005958, ~: 1926181)\n[PASS] test_allEnabledCallbacksAndRedemptionRefuseForgedCallers() (gas: 128098)\n[PASS] test_deferredFeeCreditsHarvestWhenDelivered() (gas: 1012086)\n[PASS] test_exactInputSellWithReachableLimitStillWorks() (gas: 389945)\n[PASS] test_exactOutputPartialSellChargesOnlyRealisedInput() (gas: 422505)\n[PASS] test_exactOutputSellPaysSameLaunchFeeAsExactInput() (gas: 775793)\n[PASS] test_feeDeltasRejectOverflowInsteadOfWrapping() (gas: 95974)\n[PASS] test_gameIsExemptForAllFourSwapShapes() (gas: 216646)\n[PASS] test_hookDataCannotSpoofGameExemption() (gas: 139595)\n[PASS] test_largeExactOutputSellAlsoDefersFee() (gas: 973776)\n[PASS] test_largeSellDefersThenRedeemsEntireFee() (gas: 1128588)\n[PASS] test_partialFillSellRevertsAtomically() (gas: 353603)\n[PASS] test_redemptionCanPayPartThenFinish() (gas: 1226895)\n[PASS] test_smallestSellRoundsFeeDownWithoutReportingIt() (gas: 343846)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 273.80ms (413.23ms CPU time)\n\nRan 13 tests for test/GammaSettlementProperties.t.sol:GammaSettlementPropertiesTest\n[PASS] testFuzz_claimFeeIsBoundedAndNeverIncreases(uint32,uint32) (runs: 1000, μ: 37558, ~: 37599)\n[PASS] testFuzz_drawRiskIsMonotonicAndExtraEnergyAddsAtMostOneSurvivor(bytes32,uint256,uint16,uint16) (runs: 512, μ: 307610, ~: 227231)\n[PASS] testFuzz_eachEpochAccountsForItsEntireTranche(uint16[4],bytes32) (runs: 256, μ: 2027123, ~: 2013848)\n[PASS] testFuzz_roundIsFirstAtOrAfterRequiredDelay(uint32) (runs: 1000, μ: 28666, ~: 28622)\n[PASS] testFuzz_settlementCadenceCannotChangeOutcome(uint16[4],bytes32) (runs: 128, μ: 3363526, ~: 3339774)\n[PASS] test_badProofDoesNotBlockPurchaseOrInventRandomness() (gas: 1316163)\n[PASS] test_batchPaginationHandlesEmptyPagesAndKeepsNewestFirst() (gas: 1020741)\n[PASS] test_futureHarvestDoesNotReceivePastClaimBonuses() (gas: 2629990)\n[PASS] test_guardResetsPerEpochAndLiftsAtExactBoundary() (gas: 3019247)\n[PASS] test_harvestAtExactDayBoundaryUsesItsOwnUnit() (gas: 1272171)\n[PASS] test_maturedClaimSkipsUntilTheExact24HourBoundary() (gas: 1352542)\n[PASS] test_oneUnitDrawsOneBatchAndFinalizationNeedsAnotherUnit() (gas: 1785624)\n[PASS] test_settlementWorkIsClampedTo256EvenForMaximumInput() (gas: 68778329)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 273.77ms (724.88ms CPU time)\n\nRan 9 tests for test/GammaClaimBonus.t.sol:GammaClaimBonusTest\n[PASS] testFuzz_anySplitPaysTheSame(uint8,uint8) (runs: 512, μ: 3183658, ~: 3146748)\n[PASS] test_batchesHarvestedLaterAreNotPenalised() (gas: 3592584)\n[PASS] test_bonusFromOthersFeesIsStillPaid() (gas: 1980452)\n[PASS] test_loneClaimantSplitSendsTheThirdToTheHarvest() (gas: 1869051)\n[PASS] test_selfIndexCheckpointsOnlyOnFeeSharing() (gas: 1133810)\n[PASS] test_splitClaimPaysTheSameAsOneClaim() (gas: 2747127)\n[PASS] test_sweepCreditsStrayGammaToTheHarvest() (gas: 239265)\n[PASS] test_sweepNeedsInitialize() (gas: 44217)\n[PASS] test_sweptGammaIsPaidOutThroughTheHarvest() (gas: 1454496)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 273.93ms (281.23ms CPU time)\n\nRan 7 tests for test/DrandBeaconAdversarial.t.sol:DrandBeaconAdversarialTest\n[PASS] testFuzz_rejectsEveryNoncanonicalLength(uint8,uint64) (runs: 1000, μ: 37495, ~: 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 273.90ms (294.28ms 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           | 360   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | buy               | 307   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | buyIntoKnownEpoch | 346   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | claimExact        | 358   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | claimLagging      | 353   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | donate            | 348   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | foreignClaim      | 315   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | settle            | 354   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | sweep             | 331   | 0       | 0        |\n╰--------------------------+-------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.10s (1.09s 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      | 799   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| GammaHandler | claim    | 802   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| GammaHandler | proveAll | 837   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| GammaHandler | trade    | 814   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| GammaHandler | warp     | 844   | 0       | 0        |\n╰--------------+----------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.10s (1.10s 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            | 1755  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | buy                | 1680  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | claim              | 1831  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | donate             | 1758  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | rejectForeignClaim | 1699  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | settle             | 1820  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | trade              | 1745  | 0       | 0        |\n╰------------------------+--------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 10.10s (10.09s CPU time)\n\nRan 24 test suites in 10.10s (14.75s CPU time): 163 tests passed, 0 failed, 0 skipped (163 total tests)\n","passed":true},{"durationMs":78,"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.redeemFees()\",\"GammaHook.unlockCallback(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\":457,\"PROJECT.md\":180,\"README.md\":136,\"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\":32,\"launch/00-audit.json\":6,\"launch/01-game.json\":23,\"launch/02-hook.json\":28,\"launch/2215-game-manifest.json\":19,\"launch/pay.sh\":45,\"script/DeployLocal.s.sol\":93,\"script/HookMiner.sol\":23,\"script/InitializeGame.s.sol\":36,\"src/DrandBeacon.sol\":58,\"src/GammaGame.sol\":741,\"src/GammaHook.sol\":251,\"src/GammaToken.sol\":14,\"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/GammaHookAudit.t.sol\":389,\"test/GammaHookLaunch.t.sol\":202,\"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\":206},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"ab035bc984b8c94ec4d88e101425fd8c06b8de4761f9d13e32293b6b39e3dd07","verifiedTreeHash":"7c24fcd2512dcc15e57167daa1e895df5c8c88b6","verifierVersion":"0.1.0+a6ee6548"},{"checks":[{"durationMs":245522,"exitCode":0,"name":"build","output":"Compiling 112 files with Solc 0.8.26\nSolc 0.8.26 finished in 245.38s\nCompiler run successful!\n","passed":true},{"durationMs":11469,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 2 tests for test/GammaHookLaunch.t.sol:GammaTokenLaunchTest\n[PASS] test_neitherFactoryNorHolderCanMintOrConfigureToken() (gas: 273415)\n[PASS] test_supplyMetadataAndTransfer() (gas: 108102)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 821.97µs (654.14µs CPU time)\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 1.66ms (1.54ms CPU time)\n\nRan 1 test for test/GammaRevision.t.sol:GammaRevisionFeeClockTest\n[PASS] test_feeHeldAtStartUntilGameGenesis() (gas: 215999)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.07ms (301.03µs CPU time)\n\nRan 1 test for test/GammaRevisionFailurePaths.t.sol:GammaRevisionFailurePathsTest\n[PASS] test_rejectedPurchaseIntoProvenEpochMovesNothingAndNextEpochOpens() (gas: 692643)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.40ms (342.43µ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.81ms (89.32µs CPU time)\n\nRan 10 tests for test/GammaLaunch.t.sol:GammaLaunchTest\n[PASS] test_backedGammaFeeIsCredited() (gas: 846713)\n[PASS] test_configuratorCannotDrainReserveThroughItsHook() (gas: 1033277)\n[PASS] test_heldGammaFollowsEveryFlow() (gas: 1701879)\n[PASS] test_hookFeesStillFlowWithTheRealHook() (gas: 373676)\n[PASS] test_imdFeeIsRejected() (gas: 766680)\n[PASS] test_initializeRecordsHeldGamma() (gas: 25870)\n[PASS] test_initializeRejectsHookBoundToAnotherGame() (gas: 378807)\n[PASS] test_initializeRejectsHookWithoutCode() (gas: 378453)\n[PASS] test_initializeRejectsKeyOfAnotherPool() (gas: 379108)\n[PASS] test_unbackedGammaFeeIsRejected() (gas: 765373)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 5.45ms (3.68ms CPU time)\n\nRan 3 tests for test/GammaHookAudit.t.sol:GammaHookBeforeGenesisTest\n[PASS] test_preGenesisDeferredFeesCanBeRedeemedAfterInitialize() (gas: 1244652)\n[PASS] test_preGenesisDeferredFeesCanBeRedeemedBeforeInitialize() (gas: 1225210)\n[PASS] test_preGenesisFeesAreFirstHarvestSurplus() (gas: 1085086)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 5.70ms (2.60ms CPU time)\n\nRan 2 tests for test/GammaRealBeacon.t.sol:GammaRealBeaconTest\n[PASS] test_invalidRealProofIsIgnoredWithoutDrawingBatch() (gas: 627365)\n[PASS] test_realProofUnblocksSettlementAndMaturedClaim() (gas: 1087737)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 12.27ms (9.10ms CPU time)\n\nRan 11 tests for test/GammaFailurePaths.t.sol:GammaFailurePathsTest\n[PASS] test_claimArrayWithForeignBatchRevertsEveryEarlierClaim() (gas: 1458613)\n[PASS] test_duplicateBatchIdsCannotMultiplyPayoutOrBonus() (gas: 1842557)\n[PASS] test_failedDonationDoesNotFundHarvest() (gas: 107425)\n[PASS] test_failedSlippageRollsBackAutoSettlementAndTransfer() (gas: 1535378)\n[PASS] test_initializeRejectsMissingImdOrHookBeforeFunding() (gas: 77435)\n[PASS] test_initializeRollbackAllowsCorrectKeyAfterMistake() (gas: 877817)\n[PASS] test_missingAllowanceAndBalanceCannotCreateFreeEnergies() (gas: 328932)\n[PASS] test_outOfRangeClaimRevertsAndEmptyClaimsAreNoOps() (gas: 1152296)\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 15.17ms (15.05ms CPU time)\n\nRan 3 tests for test/GammaHookLaunch.t.sol:GammaHookLaunchTest\n[PASS] test_factoryDeploysAllocatesInitializesAndCollects() (gas: 8735104)\n[PASS] test_hookConstructorNeedsNoLiveDependencies() (gas: 280618)\n[PASS] test_permissionsAreExactlyTheSixRequested() (gas: 9070)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 15.44ms (14.48ms CPU time)\n\nRan 10 tests for test/GammaHook.t.sol:GammaHookTest\n[PASS] test_buyExactInFeeInGamma() (gas: 374876)\n[PASS] test_exactOutBuyIsRejected() (gas: 163184)\n[PASS] test_feeSchedule() (gas: 126009)\n[PASS] test_gamePurchasesAreExempt() (gas: 631566)\n[PASS] test_onlyHookCanReportFees() (gas: 40264)\n[PASS] test_onlyPoolManager() (gas: 38799)\n[PASS] test_secondPoolRejected() (gas: 783537)\n[PASS] test_sellExactInFeeInGamma() (gas: 326891)\n[PASS] test_sniperFeeFundsFirstHarvest() (gas: 380408)\n[PASS] test_tradingFeesReachMiners() (gas: 1531679)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 9.50ms (9.31ms 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 31.48ms (31.34ms CPU time)\n\nRan 9 tests for test/GammaEmission.t.sol:GammaEmissionTest\n[PASS] testFuzz_betweenAdds(uint32,uint32,uint32) (runs: 512, μ: 30741, ~: 29899)\n[PASS] testFuzz_monotonic(uint32,uint32) (runs: 512, μ: 14420, ~: 13957)\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 176.04ms (57.01ms 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 347.26ms (20.17ms CPU time)\n\nRan 7 tests for test/GammaHookBoundaries.t.sol:GammaHookBoundariesTest\n[PASS] testFuzz_feeNeverRisesAndStaysAtFloor(uint64,uint64) (runs: 1000, μ: 53743, ~: 53701)\n[PASS] testFuzz_partialBuyTaxesOnlyRealisedGamma(uint96,uint8) (runs: 1000, μ: 883408, ~: 883378)\n[PASS] test_everyMinuteBoundaryAndPermanentFloor() (gas: 3321636)\n[PASS] test_nonImdPairFailsWithoutConsumingInitialization() (gas: 999276)\n[PASS] test_poolCreatedAfterGenesisStartsItsOwnDecayClock() (gas: 284678)\n[PASS] test_sameTokensAtDifferentFeeCannotRebindHook() (gas: 80839)\n[PASS] test_specifiedConstructorAddressesArePreserved() (gas: 92267)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 347.24ms (396.12ms 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, μ: 62757, ~: 50254)\n[PASS] test_beaconProofsAttachedToPurchase() (gas: 1069350)\n[PASS] test_buyBounds() (gas: 108798)\n[PASS] test_buyBurnsGammaAndRecordsBatch() (gas: 519586)\n[PASS] test_buyRefundsUnusedImd() (gas: 466197)\n[PASS] test_buySlippage() (gas: 211110)\n[PASS] test_claimBeforeHarvestPaysNothing() (gas: 498520)\n[PASS] test_claimFeeSchedule() (gas: 82819)\n[PASS] test_claimMaturedSkipsYoungDays() (gas: 1226876)\n[PASS] test_claimNotOwnerReverts() (gas: 1044480)\n[PASS] test_claimPaysWinningsMinusFee() (gas: 1393314)\n[PASS] test_conservation() (gas: 4336831)\n[PASS] test_dealIsFair() (gas: 35263063)\n[PASS] test_earlyClaimersPayPatientOnes() (gas: 1997485)\n[PASS] test_emptyDayRollsIntoNextHarvest() (gas: 303257)\n[PASS] test_epochWaitsForBeacon() (gas: 913448)\n[PASS] test_gasNormalPurchase() (gas: 722940)\nLogs:\n  purchase gas (same epoch): 290916\n\n[PASS] test_gasPurchaseDoingMaxSettlement() (gas: 3979780)\nLogs:\n  purchase gas (8 large batches settled): 1687651\n\n[PASS] test_guardCapsFirstWeek() (gas: 1494594)\n[PASS] test_guardLiftsAfterAWeek() (gas: 2017168)\n[PASS] test_initializeNeedsReserve() (gas: 161034)\n[PASS] test_initializeOnlyConfigurator() (gas: 44139)\n[PASS] test_initializeOnlyOnce() (gas: 40968)\n[PASS] test_noFallbackSeedOnlyDrand() (gas: 2819184)\n[PASS] test_roundForEpochIsAfterClose() (gas: 49061)\n[PASS] test_survivalRatesMatchMachines() (gas: 24427121)\n[PASS] test_survivorsSplitShares_failsSplitHarvest() (gas: 1437484)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 347.31ms (214.08ms CPU time)\n\nRan 9 tests for test/GammaClaimBonus.t.sol:GammaClaimBonusTest\n[PASS] testFuzz_anySplitPaysTheSame(uint8,uint8) (runs: 512, μ: 3179855, ~: 3146748)\n[PASS] test_batchesHarvestedLaterAreNotPenalised() (gas: 3592584)\n[PASS] test_bonusFromOthersFeesIsStillPaid() (gas: 1980452)\n[PASS] test_loneClaimantSplitSendsTheThirdToTheHarvest() (gas: 1869051)\n[PASS] test_selfIndexCheckpointsOnlyOnFeeSharing() (gas: 1133810)\n[PASS] test_splitClaimPaysTheSameAsOneClaim() (gas: 2747127)\n[PASS] test_sweepCreditsStrayGammaToTheHarvest() (gas: 239265)\n[PASS] test_sweepNeedsInitialize() (gas: 44217)\n[PASS] test_sweptGammaIsPaidOutThroughTheHarvest() (gas: 1454496)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 347.43ms (354.72ms CPU time)\n\nRan 7 tests for test/DrandBeaconAdversarial.t.sol:DrandBeaconAdversarialTest\n[PASS] testFuzz_rejectsEveryNoncanonicalLength(uint8,uint64) (runs: 1000, μ: 37496, ~: 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 347.43ms (361.36ms CPU time)\n\nRan 3 tests for test/GammaHookOneSidedLaunch.t.sol:GammaHookOneSidedLaunchTest\n[PASS] testFuzz_firstBuyNeedsNoPrefundedImdOrEth(uint96,uint8) (runs: 1000, μ: 635131, ~: 635169)\n[PASS] test_firstBuyBeforeGenesisPaysGammaAndBecomesDayZeroHarvest() (gas: 690371)\n[PASS] test_sellWithNoBuySideLiquidityRevertsWithoutChargingFee() (gas: 1147206)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 356.40ms (355.96ms CPU time)\n\nRan 15 tests for test/GammaHookAudit.t.sol:GammaHookGamma1AuditTest\n[PASS] testFuzz_exactOutputSellPaysScheduledFraction(uint96,uint16) (runs: 512, μ: 457940, ~: 457926)\n[PASS] testFuzz_multipleFeesRemainFullyBacked(uint96,uint8,bool) (runs: 512, μ: 2005562, ~: 1926201)\n[PASS] test_allEnabledCallbacksAndRedemptionRefuseForgedCallers() (gas: 128098)\n[PASS] test_deferredFeeCreditsHarvestWhenDelivered() (gas: 1012086)\n[PASS] test_exactInputSellWithReachableLimitStillWorks() (gas: 389945)\n[PASS] test_exactOutputPartialSellChargesOnlyRealisedInput() (gas: 422505)\n[PASS] test_exactOutputSellPaysSameLaunchFeeAsExactInput() (gas: 775793)\n[PASS] test_feeDeltasRejectOverflowInsteadOfWrapping() (gas: 95974)\n[PASS] test_gameIsExemptForAllFourSwapShapes() (gas: 216646)\n[PASS] test_hookDataCannotSpoofGameExemption() (gas: 139595)\n[PASS] test_largeExactOutputSellAlsoDefersFee() (gas: 973776)\n[PASS] test_largeSellDefersThenRedeemsEntireFee() (gas: 1128588)\n[PASS] test_partialFillSellRevertsAtomically() (gas: 353603)\n[PASS] test_redemptionCanPayPartThenFinish() (gas: 1226895)\n[PASS] test_smallestSellRoundsFeeDownWithoutReportingIt() (gas: 343846)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 356.35ms (517.36ms CPU time)\n\nRan 14 tests for test/GammaStateMachine.t.sol:GammaEdgeCasesTest\n[PASS] testFuzz_claimFeeEndpoints(uint256,uint256) (runs: 1000, μ: 20490, ~: 20598)\n[PASS] testFuzz_failRateIsClampedToAir(uint256) (runs: 1000, μ: 6024, ~: 6022)\n[PASS] testFuzz_survivorEdgesOfFailRate(bytes32,uint256,uint256) (runs: 1000, μ: 158565, ~: 161755)\n[PASS] test_claimMaturedBeforeHarvestPaysNothing() (gas: 832212)\n[PASS] test_claimedBatchCollectsNoLaterBonus() (gas: 1989028)\n[PASS] test_feeReportFromConfiguratorOrPoolManagerIsRejected() (gas: 110903)\n[PASS] test_hookCannotCreditTheSameTokensTwice() (gas: 156801)\n[PASS] test_machineAndAmountEdgesAreRejected() (gas: 621781)\n[PASS] test_noSettlementWithoutBeaconEvenAfterManyDays() (gas: 910591)\n[PASS] test_settleWithZeroUnitsMovesNothing() (gas: 603073)\n[PASS] test_strayTransferSweptOnceOnly() (gas: 153240)\n[PASS] test_sweepWithNothingStrayReturnsZeroAndChangesNothing() (gas: 91933)\n[PASS] test_zeroHookFeeIsANoOp() (gas: 111306)\n[PASS] test_zeroImdLimitCannotBuyAndKeepsNoTokens() (gas: 213401)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 356.41ms (245.94ms CPU time)\n\nRan 15 tests for test/GammaHookAudit.t.sol:GammaHookGamma0AuditTest\n[PASS] testFuzz_exactOutputSellPaysScheduledFraction(uint96,uint16) (runs: 512, μ: 462276, ~: 462344)\n[PASS] testFuzz_multipleFeesRemainFullyBacked(uint96,uint8,bool) (runs: 512, μ: 2004744, ~: 1922847)\n[PASS] test_allEnabledCallbacksAndRedemptionRefuseForgedCallers() (gas: 128373)\n[PASS] test_deferredFeeCreditsHarvestWhenDelivered() (gas: 1007880)\n[PASS] test_exactInputSellWithReachableLimitStillWorks() (gas: 395856)\n[PASS] test_exactOutputPartialSellChargesOnlyRealisedInput() (gas: 427709)\n[PASS] test_exactOutputSellPaysSameLaunchFeeAsExactInput() (gas: 785894)\n[PASS] test_feeDeltasRejectOverflowInsteadOfWrapping() (gas: 95995)\n[PASS] test_gameIsExemptForAllFourSwapShapes() (gas: 216786)\n[PASS] test_hookDataCannotSpoofGameExemption() (gas: 140004)\n[PASS] test_largeExactOutputSellAlsoDefersFee() (gas: 969333)\n[PASS] test_largeSellDefersThenRedeemsEntireFee() (gas: 1124382)\n[PASS] test_partialFillSellRevertsAtomically() (gas: 359011)\n[PASS] test_redemptionCanPayPartThenFinish() (gas: 1222689)\n[PASS] test_smallestSellRoundsFeeDownWithoutReportingIt() (gas: 351913)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 356.43ms (677.61ms CPU time)\n\nRan 13 tests for test/GammaSettlementProperties.t.sol:GammaSettlementPropertiesTest\n[PASS] testFuzz_claimFeeIsBoundedAndNeverIncreases(uint32,uint32) (runs: 1000, μ: 37557, ~: 37599)\n[PASS] testFuzz_drawRiskIsMonotonicAndExtraEnergyAddsAtMostOneSurvivor(bytes32,uint256,uint16,uint16) (runs: 512, μ: 301973, ~: 210293)\n[PASS] testFuzz_eachEpochAccountsForItsEntireTranche(uint16[4],bytes32) (runs: 256, μ: 2028360, ~: 2009856)\n[PASS] testFuzz_roundIsFirstAtOrAfterRequiredDelay(uint32) (runs: 1000, μ: 28667, ~: 28622)\n[PASS] testFuzz_settlementCadenceCannotChangeOutcome(uint16[4],bytes32) (runs: 128, μ: 3371182, ~: 3330673)\n[PASS] test_badProofDoesNotBlockPurchaseOrInventRandomness() (gas: 1316163)\n[PASS] test_batchPaginationHandlesEmptyPagesAndKeepsNewestFirst() (gas: 1020741)\n[PASS] test_futureHarvestDoesNotReceivePastClaimBonuses() (gas: 2629990)\n[PASS] test_guardResetsPerEpochAndLiftsAtExactBoundary() (gas: 3019247)\n[PASS] test_harvestAtExactDayBoundaryUsesItsOwnUnit() (gas: 1272171)\n[PASS] test_maturedClaimSkipsUntilTheExact24HourBoundary() (gas: 1352542)\n[PASS] test_oneUnitDrawsOneBatchAndFinalizationNeedsAnotherUnit() (gas: 1785624)\n[PASS] test_settlementWorkIsClampedTo256EvenForMaximumInput() (gas: 68778329)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 356.34ms (1.29s 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      | 799   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| GammaHandler | claim    | 802   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| GammaHandler | proveAll | 837   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| GammaHandler | trade    | 814   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| GammaHandler | warp     | 844   | 0       | 0        |\n╰--------------+----------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.24s (1.23s 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           | 360   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | buy               | 307   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | buyIntoKnownEpoch | 346   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | claimExact        | 358   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | claimLagging      | 353   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | donate            | 348   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | foreignClaim      | 315   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | settle            | 354   | 0       | 0        |\n|--------------------------+-------------------+-------+---------+----------|\n| GammaStateMachineHandler | sweep             | 331   | 0       | 0        |\n╰--------------------------+-------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.24s (1.23s CPU time)\n\nRan 2 tests for test/GammaHookLedgerInvariant.t.sol:GammaHookLedgerGamma1Test\n[PASS]\nGammaHookLedgerGamma1Test invariants:\n[PASS] invariant_everyAssessedFeeIsDeliveredOrBackedByClaims\n[PASS] invariant_fixedSupplyAndNoFeeLeakage\n[PASS] invariant_poolBindingAndFeeBoundsNeverChange\n GammaHookLedgerGamma1Test invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭-----------------+----------------------+-------+---------+----------╮\n| Contract        | Selector             | Calls | Reverts | Discards |\n+=====================================================================+\n| GammaHookLedger | advance              | 1304  | 0       | 0        |\n|-----------------+----------------------+-------+---------+----------|\n| GammaHookLedger | redeem               | 1353  | 0       | 0        |\n|-----------------+----------------------+-------+---------+----------|\n| GammaHookLedger | rejectExactOutputBuy | 1349  | 0       | 0        |\n|-----------------+----------------------+-------+---------+----------|\n| GammaHookLedger | rejectForgedCallback | 1340  | 0       | 0        |\n|-----------------+----------------------+-------+---------+----------|\n| GammaHookLedger | sellExactOutput      | 1431  | 0       | 0        |\n|-----------------+----------------------+-------+---------+----------|\n| GammaHookLedger | tradeExactInput      | 1415  | 0       | 0        |\n╰-----------------+----------------------+-------+---------+----------╯\n\n[PASS] test_ledgerActionsAreLive() (gas: 2093328)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 3.31s (3.31s CPU time)\n\nRan 2 tests for test/GammaHookLedgerInvariant.t.sol:GammaHookLedgerGamma0Test\n[PASS]\nGammaHookLedgerGamma0Test invariants:\n[PASS] invariant_everyAssessedFeeIsDeliveredOrBackedByClaims\n[PASS] invariant_fixedSupplyAndNoFeeLeakage\n[PASS] invariant_poolBindingAndFeeBoundsNeverChange\n GammaHookLedgerGamma0Test invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭-----------------+----------------------+-------+---------+----------╮\n| Contract        | Selector             | Calls | Reverts | Discards |\n+=====================================================================+\n| GammaHookLedger | advance              | 1304  | 0       | 0        |\n|-----------------+----------------------+-------+---------+----------|\n| GammaHookLedger | redeem               | 1353  | 0       | 0        |\n|-----------------+----------------------+-------+---------+----------|\n| GammaHookLedger | rejectExactOutputBuy | 1349  | 0       | 0        |\n|-----------------+----------------------+-------+---------+----------|\n| GammaHookLedger | rejectForgedCallback | 1340  | 0       | 0        |\n|-----------------+----------------------+-------+---------+----------|\n| GammaHookLedger | sellExactOutput      | 1431  | 0       | 0        |\n|-----------------+----------------------+-------+---------+----------|\n| GammaHookLedger | tradeExactInput      | 1415  | 0       | 0        |\n╰-----------------+----------------------+-------+---------+----------╯\n\n[PASS] test_ledgerActionsAreLive() (gas: 2092361)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 3.35s (3.34s 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            | 1755  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | buy                | 1680  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | claim              | 1831  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | donate             | 1758  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | rejectForeignClaim | 1699  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | settle             | 1820  | 0       | 0        |\n|------------------------+--------------------+-------+---------+----------|\n| GammaAccountingHandler | trade              | 1745  | 0       | 0        |\n╰------------------------+--------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 11.33s (11.32s CPU time)\n\nRan 28 test suites in 11.34s (24.26s CPU time): 177 tests passed, 0 failed, 0 skipped (177 total tests)\n","passed":true},{"durationMs":144,"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.redeemFees()\",\"GammaHook.unlockCallback(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\":457,\"PROJECT.md\":180,\"README.md\":136,\"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/00-audit.json\":6,\"launch/01-game.json\":23,\"launch/02-hook.json\":28,\"launch/2215-game-manifest.json\":19,\"launch/pay.sh\":45,\"script/DeployLocal.s.sol\":93,\"script/HookMiner.sol\":23,\"script/InitializeGame.s.sol\":36,\"src/DrandBeacon.sol\":58,\"src/GammaGame.sol\":741,\"src/GammaHook.sol\":251,\"src/GammaToken.sol\":14,\"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/GammaHookAudit.t.sol\":389,\"test/GammaHookBoundaries.t.sol\":186,\"test/GammaHookCoverage.md\":25,\"test/GammaHookLaunch.t.sol\":202,\"test/GammaHookLedgerInvariant.t.sol\":134,\"test/GammaHookOneSidedLaunch.t.sol\":168,\"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\":206,\"test/utils/GammaHookLedger.sol\":286},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"fb450ad3667c5933aee71a05b573e38464de2ffdc735437f8e3198f6ce90bda4","verifiedTreeHash":"3137063847bc54b7dd64fb1b1771f226f9437346","verifierVersion":"0.1.0+a6ee6548"}]}