{"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":"a3749636-85a4-4a35-be39-39a7c315d460","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":null,"dependsOn":["audit_imported_code"],"execution":{"mustProduce":["src/LaunchToken.sol"],"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":"failed"},{"acceptedSubmissionHash":null,"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":"waiting"},{"acceptedSubmissionHash":null,"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":"waiting"},{"acceptedSubmissionHash":"329539f60ec286bc3ad2dca579f98a8ff578b7668ac107f6bed862d83b58dd92","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":null,"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":"waiting"},{"acceptedSubmissionHash":null,"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":"waiting"},{"acceptedSubmissionHash":null,"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":"waiting"},{"acceptedSubmissionHash":null,"dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"waiting"},{"acceptedSubmissionHash":null,"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":"waiting"}],"objective":"BLACKOUT v3.17 L2 on Robinhood Chain (4663). NO SOURCE CHANGES.\nSource: commit e17f0ef7bd7d188949eb4dc91e3eb0540c94cbf3 of identity-md-launches/launch-1544-blackout-swarmdesk-bootstrap-robinhood. Build with the unchanged repo foundry.toml: solc 0.8.26, optimizer 200, cancun, viaIR false, bytecode_hash none.\nL1 is deployed as launch 1628: SwarmDesk 0x5cb0686a4475d1f8a6c6e5240579080a4a05b609, EndowmentEscrow 0x11947494c0ba91508eb7409648193818c36521fb.\nDeploy the platform token plus V4Market hook using onchain univ4_hook, pairWith eth, economics poolBps 7000 and remainderTo 0x11947494c0ba91508eb7409648193818c36521fb (the L1 EndowmentEscrow). Platform pool fee 12500, tick spacing 60. Paying wallet 0x6d51cBE774aeAe4dd40bCA2560E9bc2695A0d007 receives the platform 1% via LaunchFactory.claimFees; the platform keeps 0.25%. The hook adds its 1% rewards share after the stepped launch tax and its existing sell surcharge. Omit owner and initialMarketCapWei. Return token, hook, launch number and pool key.\nGAME_CONFIG_HASH: 0x11ed7438974e96ecc962201e9c46422c7bf239d725876863fa25a56c734b20d3.\nExact ordered constructor words and expected creation hash:\n[0] V4Market [\"$poolManager\",\"$token\",\"12500\",\"60\",\"$factory\",\"0x5cb0686a4475d1f8a6c6e5240579080a4a05b609\",\"0x11ed7438974e96ecc962201e9c46422c7bf239d725876863fa25a56c734b20d3\"]\nCreation hash: 0x17e05386811fe526f0f44b95338a881b74127920e0f0587dbd1f798028b09931\nNo literal IMD protocol addresses may appear as constructor words. Protocol endpoints are compiled in; allowed platform placeholders stay as shown.\nAccepted findings (low/info only, full text in docs/accepted-v317.md; do not change source for them):\n- RewardPool shortfall: repair with a plain token transfer; donate is not the repair path.\n- Exact-input swaps only: V4Market rejects exact-output swaps.\n- spot() bounds: zero, underflowed or above-1e40 prices revert.\n- Fixed oracle signer/endpoints: compiled in, no owner rotation.\n- Answers between job A and job B: stored after L3 binds, executed only after all L4 modules are registered; stream-0 answers before role 1 revert NotConfigured and are re-presented later.\n- Future-block randomness: native L2 block entropy, not VRF; enhancement attempts lapse to their worst outcome; grid events re-arm.\n- Reserved Params keys: no gameplay effect.\n- M-2 matching twin: only the pinned binder can bind.\n- Binder liveness: one-time bind by 0x6d51…d007; the binder cannot bypass any proof.\n- GameRegistry.register prefill by a byte-identical copy: keeper registers every role immediately and verifies module(role).\n- Season.rollover before finalize: keeper finalizes, then rolls over weekly.\n- FeeRouter.executeSells sweep rollback: repeats on the next success.\n- FarmRewards same-epoch vintage back-dating: at most about 54 bps, self-triggered.\n- Deploy-gate tool takes only the JSON plan.\n- Civic.claimWar rounding dust stays in the game.\n- No max-cost bound on openPack/buyTokenPack.\n- Unused minter/caller permissions incl. Season.recordSpend admitting GridOperator: every allowed caller is a pinned module.\n- Dead fields and the stale FarmRewards budget comment (code reserves 159% against at most 157.5%).\n- PackSale.buyPacksFor gifting: value only flows in.\n- One-shot escrow: forward runs once after the whole remainder has landed.\n- Economy._insert 33-tie lock griefing: no funds at risk.\n- Festival severity band always +15%.\n- L1 judge 9cab6f32 low: PackSale.resolve sets Mythic eligibility at resolve time, so a sub-level-10 player can defer a 0.3% Mythic-eligible roll until level 10; no funds move.\n- L1 judge 9cab6f32 low: SwarmDesk.execute is permissionless and an early call inside the Params cooldown drops that week's single bounded step; the keeper executes after cooldown.\n- askBackup: anyone may spend the Desk's donated IMD once per slot, bounded by the 0.5 IMD cap.\nProject: https://playblackout.xyz\n\nToken name: BLACKOUT\nToken symbol: BLACKOUT","parentJobId":null,"planHash":"ecd3c7e154e5f2bb7b917fea159ab7b2d6db381455a56ec3d3f51525c1676d16","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"a3749636-85a4-4a35-be39-39a7c315d460","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-1642-blackout"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51338","feedbackHash":"4cf73f03bd6c57da47575d24e5deebea00bda787ce71f1c85deeca7b6f32d7e8","nodeKey":"audit_imported_code","submissionHash":"329539f60ec286bc3ad2dca579f98a8ff578b7668ac107f6bed862d83b58dd92","tag1":"review:submission","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"29a6741449da0bb3ee42d2fa35efb30058dcd6814b9cfa60487338073273b3de","state":"blocked","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"789312fc56d3f446","findings":[{"citation":"resolved","description":"beforeSwap computes the hook fee from p.amountSpecified and takes it from the PoolManager before the swap runs (manager.take + BeforeSwapDelta fee<<128). Uniswap v4 exact-input swaps stop at params.sqrtPriceLimitX96, so when the limit is hit the pool consumes only part of (ethIn - fee), but the hook keeps the fee computed on the whole specification. afterSwap never reconciles against the real swapDelta. During the 30-minute launch window (9000/5000/2500 bps) a swapper whose limit is reached loses up to 90% of an amount the pool never swapped; after the window it is 1% of the unconsumed amount. A swapper does not need to choose a tight limit deliberately: any integration that derives sqrtPriceLimitX96 from a slippage tolerance can be pushed to its limit by a front-running buy, and the victim then pays the tax on the full specification while receiving a sliver of tokens. The excess goes to heldLaunchTax/heldBase (reward buys), so no attacker profits directly; it is a loss for the swapper and a broken guarantee ('exactly PROJECT_BPS of the swap'). Uniswap's own routers pass the extreme price limit and are not affected. Fix (bytecode change, re-pin needed): compute the hook share in afterSwap from the actual consumed amount0 (-swapDelta.amount0) and return it as the unspecified/specified delta there, or in beforeSwap take nothing and settle the difference in afterSwap.","line":404,"path":"src/V4Market.sol","reproduction":"Real PoolManager (vendor/v4-core), pool initialized at sqrtPrice 2^96 by `launcher`, 1000e18 full-range liquidity, hook bound. Within 10 minutes of afterInitialize, swapper calls manager.swap(key, SwapParams(zeroForOne=true, amountSpecified=-10 ether, sqrtPriceLimitX96 = 2^96*9995/10000)). Observed with test/scratch/PartialFill.t.sol: swapper's ETH balance drops by 9.5066 ETH, it receives 0.5e18 tokens, hook.heldLaunchTax() == 9 ether. Expected: tax of 90% of the ~0.5066 ETH actually swapped (about 0.456 ETH), total spend about 0.96 ETH. Same shape after the window: 100 ether specified with the same limit costs 1.5066 ETH and hook.heldBase() == 1 ether, i.e. 1 ETH fee on a 0.5 ETH fill.","severity":"medium","snippet":"        uint256 ethIn = uint256(-p.amountSpecified);\n        uint256 taxBps = currentBuyTaxBps();\n        uint256 fee = ethIn * taxBps / 10000;","title":"V4Market charges the buy tax on the full specified input, not on what the pool consumed, so a price-limited or front-run exact-input buy pays up to 90% of unconsumed ETH"},{"citation":"resolved","description":"PoolManager.take transfers native ETH immediately out of the manager's balance; the swapper's own ETH only arrives at settle(), after swap() returns. The LaunchFactory seeds this pool with the launch token only, so the pool contributes no ETH to the manager at launch. Every launch-window buy therefore needs the PoolManager to already hold 0.9x (then 0.5x, 0.25x) of the buy from other pools, and a successful buy grows that float only by the untaxed 10%. On Robinhood Chain the pinned PoolManager 0x8366...0951 held about 24,535 ETH at review time, so no realistic buy hits the limit there; it would bite on a fresh chain, a Sepolia rehearsal against an empty manager, or any future migration to a manager with a thin float. No funds move wrongly; the swap simply reverts. The same ordering applies to the 1% share after the window (needs 1% float). If the hook is ever re-pinned, prefer taking the hook share in afterSwap or minting an ERC-6909 claim and taking at flushFees, so the fee is paid out of the swapper's own settlement.","line":408,"path":"src/V4Market.sol","reproduction":"test/scratch/TakeFloat.t.sol with the real vendor/v4-core PoolManager: initialize at 2^96 from `launcher`, add 1000e18 liquidity in [-887220, -60] (token-only, manager ETH balance 0), bind the router. Within the first 10 minutes manager.unlock -> swap(zeroForOne, -1 ether, MIN_SQRT+1) reverts (NativeTransferFailed inside take). After vm.deal(manager, 0.9 ether) the same buy succeeds with heldLaunchTax == 0.9 ether and the manager balance is 1 ether; a following 1.2 ether buy (tax 1.08 ether > 1 ether float) reverts again. At 30 minutes a 1 ether buy reverts with a 0 float and succeeds once the manager holds 0.01 ether. Expected by a swapper: an exact-input buy with any valid limit succeeds regardless of other pools' balances.","severity":"low","snippet":"        if (sinceLaunch() < LAUNCH_TAX_DECAY) {\n            manager.take(address(0), address(this), fee);\n            heldLaunchTax += fee;\n            emit LaunchTax(sender, ethIn, taxBps, fee);","title":"Buy tax is taken from the PoolManager in beforeSwap, before the swapper settles, so a buy reverts whenever the shared PoolManager ETH float is below the tax (90% of the buy in the first 10 minutes)"},{"citation":"resolved","description":"_observe writes one two-slot Observation per elapsed minute since updatedAt, capped at 32 minutes. The cap bounds the loop, so this is not a denial of service, but the whole cost lands on whichever swapper happens to trade first after the pool has been quiet for 32 minutes or more, on every swap path (beforeSwap for user swaps, _sellCallback/unlockCallback for system trades). The keeper's FeeRouter.executeBuys/executeSells therefore pays this surcharge whenever the pool is idle, which is exactly when the heartbeat bounty is least able to cover gas. No funds are at risk; on Robinhood Chain gas is cheap. Mentioned so the adapter and keeper budget for it; no source change is required.","line":258,"path":"src/V4Market.sol","reproduction":"test/scratch/ObserveGas.t.sol against the real PoolManager: two 1 ETH buys in the same minute cost 162,152 gas for the second; after vm.warp(+2 hours) the next identical buy costs 598,384 gas (first fill of the 32 slots) and after another 2 hours 564,183 gas (overwrite). Expected by the design: bounded, but the keeper should set its gas-price ceiling with the 600k figure, not 160k.","severity":"info","snippet":"        for (uint256 minute = first; minute <= last; ++minute) {\n            uint256 c = cumulative + (1e60 / lastPrice) * (minute * 60 - updatedAt);\n            observations[minute % 32] = Observation(uint64(minute), c);\n        }","title":"First swap after an idle pool back-fills up to 32 observation slots and costs about 3.7x a warm swap"},{"citation":"resolved","description":"Read in full and traced against vendor/v4-core PoolManager and Hooks: src/V4Market.sol (the L2 hook this launch deploys), src/FeeRouter.sol (the hook's only privileged counterparty: receive, depositBaseFee/SellTax/LaunchTax, executeBuys/executeSells), src/GameRegistry.sol, src/lib/Robinhood.sol, src/lib/Errors.sol, src/interfaces/IV4.sol, and the three protected floor tests. Checked: forge build at the unchanged foundry.toml (solc 0.8.26, 200 runs, cancun, viaIR false, bytecode_hash none) is clean; the V4Market creation bytecode keccak is 0x17e05386811fe526f0f44b95338a881b74127920e0f0587dbd1f798028b09931, matching the brief; runtime 21,259 bytes (under EIP-170) with no DELEGATECALL, CALLCODE or SELFDESTRUCT after PUSH-skipping; the constructor makes no external call and only validates its seven words, so it runs on an empty chain; there is no owner, initializer, pause, mint, blacklist or proxy in the hook; HOOK_FLAGS 0x10cc equals afterInitialize|beforeSwap|afterSwap|beforeSwapReturnsDelta|afterSwapReturnsDelta and the hook/pool delta accounting (take in beforeSwap offset by BeforeSwapDelta fee<<128, take in afterSwap offset by the returned unspecified delta) nets to zero at unlock; receive only accepts the PoolManager so address(hook).balance == heldBase+heldSellTax+heldLaunchTax on every path; TWAP ring buffer of 32 minutes was traced for missing-observation reverts and none is reachable once 31 minutes have passed; sell path checks exact token consumption and the 0.1 ETH/0.1% depth batch cap; the 15% total sell cap holds for any Params values. Existing suites V4Integration, StandardV4Integration, HookBinding, Market, FeesParams run green (26 tests). No literal IMD protocol address appears among the constructor words; $poolManager, $token, $factory are platform placeholders and 0x5cb0...b609 is the L1 SwarmDesk. src/LaunchToken.sol does not exist in this tree; the adapter must author the fixed-supply 1e27 BLACKOUT/BLACKOUT token (the brief expects this and it is not a defect of the delivered hook). Only skimmed for forbidden patterns (grep for delegatecall/selfdestruct/pause/mint/owner) and not reviewed line by line within the budget: Billboards, Civic, Economy, EndowmentEscrow (already deployed on L1), Enhancement, FarmCore, FarmEngine, FarmItems, FarmRewards, GridOperator, MarketFactory, PackSale, Params, RewardPool, Season, StandardAdapter, StandardMarket, StarterClaim, SwarmDesk, oracle/*. Those are job A/B contracts launched later through the ProjectFactory, not part of this L2 token-plus-hook launch; PackSale.pause is a 72-hour purchase pause by the Desk only and never touches token balances. The accepted-findings list in docs/accepted-v317.md was not re-litigated.","line":38,"path":"src/V4Market.sol","reproduction":"Not a defect. Commands run: forge build; forge test --match-path 'test/{V4Integration,StandardV4Integration,HookBinding,Market,FeesParams}.t.sol' (26 passed); cast keccak of out/V4Market.sol/V4Market.json bytecode.object; opcode scan of deployedBytecode.object.","severity":"info","snippet":"contract V4Market is ReentrancyGuard {","title":"Coverage: contracts read, checks run, and what was not reached"}],"hash":"329539f60ec286bc3ad2dca579f98a8ff578b7668ac107f6bed862d83b58dd92","nodeId":"fd0692e9-1a7e-4bee-9278-9e070da9c4bc","outcome":"completed","summary":"Review complete. The findings file at `.imd-findings.json` holds four entries, and no source file was changed. Git reports a clean tree because the findings file and `test/scratch/` are ignored or untracked.\n\n**Outcome: no critical or high defect in the launch hook.** The V4Market build at the unchanged profile is clean, its creation hash matches the expected `0x17e05386…b09931`, its runtime is 21,259 bytes with no DELEGATECALL, CALLCODE or SELFDESTRUCT, the constructor makes no external calls, and the hook has no owner, initializer, pause, mint or proxy. The five hook suites pass with 26 tests. Constructor words contain no literal IMD protocol address.\n\n**Findings written:**\n\n- **Medium, `src/V4Market.sol:404`.** The buy tax is computed on the full exact-input specification, never on what the pool consumed. A swap whose price limit is reached pays 90% of unconsumed ETH in the launch window. Reproduced on the real PoolManager: 10 ETH specified, about 0.5 ETH swapped, 9 ETH taken as launch tax. A front-run can push any slippage-derived limit swap into this state. Uniswap's own routers pass extreme limits and are unaffected.\n- **Low, `src/V4Market.sol:408`.** The tax is pulled from the PoolManager in beforeSwap, before the swapper settles, so a launch-window buy needs the shared manager float to already cover 90% of it. Reproduced with a token-only seeded pool and empty manager. The live Robinhood PoolManager holds about 24,535 ETH, so this only bites on a thin-float chain or rehearsal.\n- **Info, `src/V4Market.sol:258`.** The first swap after 32 idle minutes back-fills the observation ring and costs about 600k gas against 162k warm. Bounded, but the keeper's gas ceiling should budget for it.\n- **Info, coverage.** Read in full: V4Market, FeeRouter, GameRegistry, Robinhood, Errors, IV4 and the three protected tests. Only skimmed for forbidden patterns: the 18 job A/B game contracts and the oracle library, which this L2 launch does not deploy. `src/LaunchToken.sol` does not exist; the adapter must author the standard fixed-supply token.\n\nScratch reproductions are in `test/scratch/PartialFill.t.sol`, `TakeFloat.t.sol` and `ObserveGas.t.sol`. They depend on the project's test base, so they are evidence rather than attached proofs, which no finding here requires.","treeHash":null,"usage":{"cachedInputTokens":1711901,"inputTokens":546,"model":"claude-fable-5-1","outputTokens":44459,"runtime":"claude","turns":34,"wallClockMs":796295}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"5e7415761e1c9515","findings":[],"hash":"d4a0a670cd90c08798fdb240bdc23fa61e8e2c999e918e1ba7c5e00cf09ef9a0","nodeId":"b90e6c0b-5bef-41bb-887b-ba9db7b6bb95","outcome":"failed","summary":"needs input: The task requires fixing reproduced audit findings but also says NO SOURCE CHANGES and requires V4Market creation hash 0x17e05386811fe526f0f44b95338a881b74127920e0f0587dbd1f798028b09931. The checkout is exactly commit e17f0ef7bd7d188949eb4dc91e3eb0540c94cbf3 and builds to that hash. Local tests using the unchanged production V4Market and real vendored PoolManager reproduce findings 399667655ec6ac37ba747ac85fe41b6dd2991701ec533f9e7d3f7f33e1480da2 (medium: a 10 ETH specified buy with sqrtPriceLimitX96 = 2^96 * 9995 / 10000 pays 9 ETH launch tax while receiving about 0.5 tokens; after 30 minutes a 100 ETH specified buy pays 1 ETH hook fee for the same fill) and 31000f8da78c3cfca0aa109d53c8f1e9ebd2f18dbe05070e59129a5a36444c7e (low: buys revert on a token-only pool with zero PoolManager ETH float and succeed when the manager is prefunded). Neither is among the explicitly accepted findings. Correcting them requires a source and bytecode change; constructor arguments, deployment settings, or ","treeHash":null,"usage":{"cachedInputTokens":2199040,"inputTokens":84208,"model":"gpt-6-astra","outputTokens":9757,"runtime":"codex","turns":8,"wallClockMs":342892}}],"verification":[]}