{"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":"486c9374-f896-4d17-b8a0-2a901a6f355f","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"80060241b00bcddd63e45c498422c9049d672ef56118e823cbef9b4764fce094","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_economics","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"7d3ee56090a801bdf5b642d8727b18ae18eb915a6600dd301373094d18d05f47","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_flow","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":null,"dependsOn":["build_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"3014f1ea5961918ca059453a484bf4c8bcbbfc2248dbe31d94ac7c5cdf8f50bd","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"3014f1ea5961918ca059453a484bf4c8bcbbfc2248dbe31d94ac7c5cdf8f50bd","skillId":"audit-judge","state":"failed"},{"acceptedSubmissionHash":"e16b77f3d0a5b119ea29f6d4a28828aad252c8fcf9740685482089c65ce9c723","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_math","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"49796fc3430533e01d7d86c91cdee58dca4b4ae2799e242343a473fd3d848411","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_permissions","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"2b423287197ab37ba11121eceb8c3146be87c7c0525cb68279c6330796da279f","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"47381b166d1f406a34f0ebdc740ee21bc0383e56e7ad9084b26abf1bf7570904","skillId":"build-contract-project","tools":[]},"key":"build_contract_project","kind":"code","role":"implement","skillHash":"47381b166d1f406a34f0ebdc740ee21bc0383e56e7ad9084b26abf1bf7570904","skillId":"build-contract-project","state":"accepted"},{"acceptedSubmissionHash":"3867a9445dc904f9cd59b0cc984099710b11184f12aeb5fb57cf8186630fc157","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"5f515228d633989050f902541c6162cd22d06c87379edcbbd4f43edb097b0721","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","state":"accepted"}],"objective":"A launch-guard hook: for the first 24 hours after initialization, beforeSwap rejects any swap that buys more than 1% of the launch token's total supply; after that it allows everything. No owner.","parentJobId":null,"planHash":"77aab5deedcb0541ab90c77360b3ba83171e29fef0c172fb9db7e9f5d565b662","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"486c9374-f896-4d17-b8a0-2a901a6f355f","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-542-launch-guard-hook-first-24"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51018","feedbackHash":"d87354951234dc1ed11bd30c900232e0c34168799a8d978dbb274dc590aa08fb","nodeKey":"audit_economics","submissionHash":"80060241b00bcddd63e45c498422c9049d672ef56118e823cbef9b4764fce094","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"50953","feedbackHash":"4f0a39ec80d5ef77ef027d6ffa9e9f8097d9d43616db2b3764ce6afe01ae171f","nodeKey":"audit_flow","submissionHash":"7d3ee56090a801bdf5b642d8727b18ae18eb915a6600dd301373094d18d05f47","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"50975","feedbackHash":"f8fefbd63b25a09fca9155316e22021e1bf256a422855584dde5589404448e6f","nodeKey":"audit_math","submissionHash":"e16b77f3d0a5b119ea29f6d4a28828aad252c8fcf9740685482089c65ce9c723","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51725","feedbackHash":"4f0e9a6b409b1de0f4bebc1fb9f647d8ae43e1d6d8c49860b163f448e8a812ee","nodeKey":"audit_permissions","submissionHash":"49796fc3430533e01d7d86c91cdee58dca4b4ae2799e242343a473fd3d848411","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51432","feedbackHash":"cde3b3138834babc056e0d05aff0b7025e6f3ec0df9a0562ab6f89ec7eea3261","nodeKey":"build_contract_project","submissionHash":"2b423287197ab37ba11121eceb8c3146be87c7c0525cb68279c6330796da279f","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51300","feedbackHash":"4973210e1c628cc5706e36a942cd3210b9e992045aa9c94564b8259933b8c808","nodeKey":"manifest","submissionHash":"3867a9445dc904f9cd59b0cc984099710b11184f12aeb5fb57cf8186630fc157","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"50962","feedbackHash":"d705ceb79c0a27bdecb5907289347df8051701706e691b32428bd87cbac820e0","nodeKey":"manifest","submissionHash":"e63116736099bf4e37ce9cefca4381cb2c9316a7451b9cd016cf0485a0abbe67","tag1":"verification:checks","tag2":"acceptance-v2","value":0},{"agentId":"51432","feedbackHash":"30540c8291b4ca25e4cff299b5afb9fb6acae3c5076a80c2d4ad657481f6f8ab","nodeKey":"write_foundry_tests","submissionHash":"5f515228d633989050f902541c6162cd22d06c87379edcbbd4f43edb097b0721","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"c45683bbf483d132518e90eb241b74bc4182df45bc73612b4ea8dde97e1a0e94","state":"blocked","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"991bbed67e8076ff56147c79d41d15606b5978520aec8cd545737ccf494d9e2e","device":"03f15d1296244279","findings":[],"hash":"2b423287197ab37ba11121eceb8c3146be87c7c0525cb68279c6330796da279f","nodeId":"ce72edb4-09d8-4f55-b3bd-ee5ab7ee0728","outcome":"completed","summary":"Everything is delivered and committed on `main` as `9e774be`. Clean build, 35 passing tests, and `forge fmt --check` all succeed with an empty environment.\n\n**What was built**\n\n- `src/LaunchGuardHook.sol` is the launch guard. It enables `beforeInitialize`, `beforeSwap`, and `afterSwap` only, with no owner, no admin functions, and no delta or fee permissions. At initialization it records the window start, which side of the pair holds the launch token, and the cap of one percent of total supply. During the 24-hour window, `beforeSwap` rejects exact-output buys above the cap. `afterSwap` rejects any buy whose realized output exceeds the cap, which is what makes the guard hold for exact-input swaps. After the window, every swap passes.\n- `src/LaunchToken.sol` is the fixed-supply OpenZeppelin ERC-20 named \"Launch Guard\" (`LGRD`) minting 10^27 units to its deployer, with no mint, pause, owner, or upgrade path.\n- `src/HookFlags.sol` and `test/mocks/MockERC20.sol` match the paths and signatures the pinned admission tests import.\n- `script/DeployLaunch.s.sol` mines a CREATE2 salt and deploys token then hook. Its `run()` reads the environment; tests call `deploy` directly with explicit arguments.\n- Dependencies are vendored as plain files under `lib/` with licences and a `VENDORED.md` listing exact commits. No submodules.\n\n**Verification**\n\n- 35 tests across three suites cover permissions, caller refusal, initialization binding, both pair orderings, native ETH quote, the exact window boundary at the 24-hour mark, per-pool windows, rejected swaps leaving state untouched, and three fuzz tests.\n- I also ran the two pinned protected test files in scratch space against the real hook and token creation code. All nine passed, including the opcode scan and the flag agreement check.\n- Slither and Mythril were not available and did not run.\n\n**One judgment call to flag**\n\nThe brief names `beforeSwap` as the rejecting callback. I added an `afterSwap` check with the same cap and window because the output of an exact-input swap is unknown before the pool runs it, and exact input is the common router default. Without it, the stated behaviour would not hold. The README explains this, along with assumptions, deployment parameters, the Wizard-style configuration record, and operational responsibilities.\n\n**Open items for the deployer**\n\n- The hook constructor takes the PoolManager and the launch token address, so the factory must know the token address before finalizing the hook's creation code.\n- The window starts at pool initialization, so liquidity should be seeded promptly afterward.\n- An independent adversarial review is still required before the hook fronts a live pool.","treeHash":"c755c87f1a891d28a5665108518ed71271806e2c","usage":{"cachedInputTokens":2755679,"inputTokens":834,"model":"claude-fable-5-1","outputTokens":69095,"runtime":"claude","turns":46,"wallClockMs":1197413}},{"artifacts":[],"attempt":2,"bundleHash":"24d57251be44be9f9c9c6f00d31d35044efdf3862430c7b402a420950360c11d","device":"05778e691c371384","findings":[],"hash":"3867a9445dc904f9cd59b0cc984099710b11184f12aeb5fb57cf8186630fc157","nodeId":"eeaf1720-a77a-42f5-8d9f-a60eb5afd816","outcome":"completed","summary":"Created [launch.json](/root/.identitymd/work/486c9374-f896-4d17-b8a0-2a901a6f355f/eeaf1720-a77a-42f5-8d9f-a60eb5afd816/launch.json) with matching permissions, native ETH pairing, and decimal sqrtPriceX96.\n\nSchema validation, `forge build`, and all 44 tests passed, including protected checks. Only the manifest is submitted.\n\nConstructor addresses are documented local rehearsal bindings; production addresses were not supplied.","treeHash":"cdc2b60cd34d5310f4e47afc79ede9b9148b9b61","usage":{"cachedInputTokens":406656,"inputTokens":46866,"model":"gpt-6-astra","outputTokens":5564,"runtime":"codex","turns":4,"wallClockMs":203561}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"004eae350f695d24","findings":[],"hash":"49796fc3430533e01d7d86c91cdee58dca4b4ae2799e242343a473fd3d848411","nodeId":"7feebda5-546c-4222-be2c-1f7c4c2a4ccc","outcome":"completed","summary":"Wrote [.imd-findings.json](/root/nft3/.identitymd/work/486c9374-f896-4d17-b8a0-2a901a6f355f/7feebda5-546c-4222-be2c-1f7c4c2a4ccc/.imd-findings.json) with no substantiated defects and coverage for all four required entry points.\n\nAll 35 Foundry tests passed, including three fuzz tests. Production files remain unchanged. Deployment-specific manifest and constructor binding were not verified.","treeHash":null,"usage":{"cachedInputTokens":614400,"inputTokens":87116,"model":"gpt-6-astra","outputTokens":4977,"runtime":"codex","turns":4,"wallClockMs":194225}},{"artifacts":[],"attempt":1,"bundleHash":"2db019779ed8fbdba9ffe0461d1e5eff5aa352269d0e87ad0e3cbdb19dc212f2","device":"03f15d1296244279","findings":[{"description":"The cap is computed as `(supply * MAX_BUY_BPS) / BPS_DENOMINATOR`. The intermediate product `supply * 100` overflows (checked arithmetic, Panic 0x11) for any `totalSupply()` above `type(uint256).max / 100` (about 1.16e75). The manager wraps the panic in `WrappedError` and `initialize` reverts, so a pool for that token can never be opened through this hook; there is no owner and no second path. The value the hook wants, `supply / 100`, always fits. The bundled `LaunchToken` (1e27) is far from the edge, but the hook is bound to an arbitrary `launchToken` address and the README presents it as reusable for any launch token; tokens with supplies at or near `type(uint256).max` exist. Fix: compute `supply / 100` (or `Math.mulDiv(supply, MAX_BUY_BPS, BPS_DENOMINATOR)`).","line":171,"path":"src/LaunchGuardHook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {PoolManager} from \"v4-core/src/PoolManager.sol\";\nimport {IHooks} from \"v4-core/src/interfaces/IHooks.sol\";\nimport {PoolKey} from \"v4-core/src/types/PoolKey.sol\";\nimport {Currency} from \"v4-core/src/types/Currency.sol\";\nimport {LaunchGuardHook} from \"src/LaunchGuardHook.sol\";\nimport {HookFlags} from \"src/HookFlags.sol\";\nimport {HookMiner} from \"src/HookMiner.sol\";\n\n/// @dev A plain ERC-20 whose whole supply is minted to the deployer, like the launch token but with a\n/// supply chosen by the test.\ncontract SupplyToken is ERC20 {\n    constructor(uint256 supply) ERC20(\"Supply\", \"S\") {\n        _mint(msg.sender, supply);\n    }\n}\n\n/// @notice Finding: `beforeInitialize` computes `supply * MAX_BUY_BPS / BPS_DENOMINATOR`, which overflows\n/// for any token whose total supply exceeds `type(uint256).max / 100`. Such a token can never open a\n/// pool through the hook. The cap it should record, `supply / 100`, fits in a uint256 without trouble.\n///\n/// Fails on the current code (the initialize call reverts with an arithmetic panic wrapped by the\n/// manager) and passes once the cap is computed without the intermediate product.\ncontract FindingHugeSupplyOverflowTest is Test {\n    uint160 constant SQRT_PRICE_1_1 = 79228162514264337593543950336;\n    uint160 constant FLAGS = HookFlags.BEFORE_INITIALIZE | HookFlags.BEFORE_SWAP | HookFlags.AFTER_SWAP;\n\n    PoolManager manager;\n\n    function setUp() public {\n        manager = new PoolManager(address(this));\n    }\n\n    function test_tokenWithSupplyAboveUint256MaxOver100CanStillLaunch() public {\n        uint256 supply = type(uint256).max / 100 + 1;\n        SupplyToken launch = new SupplyToken(supply);\n        SupplyToken quote = new SupplyToken(1e24);\n\n        bytes memory creationCode =\n            abi.encodePacked(type(LaunchGuardHook).creationCode, abi.encode(manager, address(launch)));\n        (, bytes32 salt) = HookMiner.find(address(this), FLAGS, creationCode, 2_000_000);\n        LaunchGuardHook hook = new LaunchGuardHook{salt: salt}(manager, address(launch));\n\n        (Currency c0, Currency c1) = address(launch) < address(quote)\n            ? (Currency.wrap(address(launch)), Currency.wrap(address(quote)))\n            : (Currency.wrap(address(quote)), Currency.wrap(address(launch)));\n        PoolKey memory key = PoolKey(c0, c1, 3_000, 60, IHooks(address(hook)));\n\n        // Expected: the pool opens and the cap is 1% of the supply. Actual: arithmetic overflow revert.\n        manager.initialize(key, SQRT_PRICE_1_1);\n        (uint64 startedAt,, uint256 maxBuy) = hook.launches(key.toId());\n        assertEq(startedAt, uint64(block.timestamp));\n        assertEq(maxBuy, supply / 100);\n    }\n}","reproduction":"Deploy an ERC-20 with totalSupply = type(uint256).max / 100 + 1 and a hook bound to it at a flagged address; call `PoolManager.initialize(key, SQRT_PRICE_1_1)` for a pool containing that token. Expected: the pool opens with `launches[id].maxBuy == supply / 100`. Actual: revert `WrappedError(hook, beforeInitialize.selector, Panic(0x11), HookCallFailed())`. With totalSupply = type(uint256).max / 100 the same call succeeds, which pins the boundary to the multiplication. Run: forge test --match-path test/scratch/Finding_HugeSupplyOverflow.t.sol","severity":"low","title":"beforeInitialize overflows for a launch token whose total supply exceeds uint256.max / 100, so such a token can never open a pool through the hook"},{"description":"`Launch.startedAt == 0` means 'unknown pool' and makes `_activeBuyCap` revert `UnknownPool` for every swap, buys and sells alike, forever. `beforeInitialize` stores `uint64(block.timestamp)` without checking it is non-zero, so a pool initialized at timestamp 0 is recorded as never initialized: `isGuardActive` returns false, `guardEndsAt` returns 0, and every swap reverts. No live chain produces a zero timestamp and Foundry's default is 1, so this is unreachable in practice and reported for completeness; a one-line `if (startedAt == 0) revert` or storing a separate `initialized` flag removes the sentinel overlap.","line":223,"path":"src/LaunchGuardHook.sol","reproduction":"`vm.warp(0)`; deploy the token, hook and a pool through `manager.initialize`; add liquidity; `vm.warp(365 days)`; swap in either direction. Expected: swaps succeed (the window has long closed). Actual: every swap reverts with `WrappedError(hook, <beforeSwap selector>, UnknownPool(), HookCallFailed())`. Reproduced in test/scratch/Findings.t.sol::test_timestampZeroInitialization.","severity":"info","title":"A pool initialized in a block with timestamp 0 is permanently unswappable (startedAt == 0 doubles as the 'never initialized' sentinel)"},{"description":"Each `swap` call is judged on its own: nothing in the hook accumulates per transaction, per block, per sender or per pool. A router that runs several swaps inside one `unlock` (any custom `IUnlockCallback`) buys the cap as many times as it likes in a single transaction, paying only the price impact. This matches the brief ('any swap that buys more than 1%') and the README's 'cap is per swap' assumption, so it is not asserted wrong; it is recorded because a launch operator reading '1% per buyer for 24 hours' would expect otherwise. The delivered test `test_batchedSwapsInOneUnlockAreEachCappedAndTheBatchFailsAsAWhole` demonstrates 3% in one transaction via test/mocks/MultiSwapRouter.sol.","line":191,"path":"src/LaunchGuardHook.sol","reproduction":"During the window, call MultiSwapRouter.swapMany(keyLaunch1, zeroForOne=true, [1e25, 1e25, 1e25]). Expected by a reader of the brief: at most 1e25 of the launch token leaves the pool in that transaction. Actual: the swapper receives exactly 3e25 (3% of supply) in one transaction; only a single swap above 1e25 is refused.","severity":"info","title":"The 1% cap is per swap, so N swaps at the cap inside one unlock acquire N% of the supply atomically"},{"description":"The launch token is a plain ERC-20 with no transfer hook, and the guard lives entirely in the pool hook. Anyone can initialize a Uniswap v4 pool for the token with `hooks = address(0)` (or a v2/v3 pool, or trade OTC) and buy any amount during the 24 hours; the guarded pool only decides what its own liquidity sells. This is inherent to a hook-only design and consistent with the brief, which asked for a hook; it is recorded because the README's Assumptions do not state it and it determines what the launch guard actually guarantees: the launch pool's liquidity, not the token's distribution.","line":12,"path":"src/LaunchToken.sol","reproduction":"During the window, initialize `PoolKey(token, quote, 3000, 60, IHooks(address(0)))` on the same manager, seed it, and exact-output buy 5e25 (5% of supply). Expected by a reader of 'buys of more than 1% are rejected for 24 hours': revert. Actual: the swap succeeds; the hook is never called because the key does not reference it.","severity":"info","title":"The guard only covers pools that name the hook; the token itself is unrestricted, so any other venue trades it uncapped during the window"},{"description":"`beforeSwap` judges `amountSpecified` for exact-output buys. A request for more than the cap with a `sqrtPriceLimitX96` close to the current price would realize only a fraction of the request (the pool stops at the limit), possibly far below the cap, yet it is refused before the pool runs. The error is in the safe direction (nothing over the cap ever goes through, and `afterSwap` would catch the realized amount anyway) and it is what the brief literally names, so it is not asserted wrong. Integrators should size exact-output requests to the cap rather than rely on the price limit.","line":191,"path":"src/LaunchGuardHook.sol","reproduction":"During the window, exact-output buy of 1e25 + 1 with sqrtPriceLimitX96 = current price * (1 - 1/1000). The pool would deliver roughly 2e23 (0.02% of supply). Actual: revert `BuyExceedsLaunchCap(1e25 + 1, 1e25)` from beforeSwap. Delivered test: `test_exactOutputRequestOverTheCapIsRejectedBeforeThePriceLimitCanShortenIt`.","severity":"info","title":"Exact-output requests above the cap are refused in beforeSwap even when the swap's price limit would have delivered less than the cap"}],"hash":"5f515228d633989050f902541c6162cd22d06c87379edcbbd4f43edb097b0721","nodeId":"23b48a46-c1fa-460d-95e0-32b946531c85","outcome":"completed","summary":"Done. The hook holds up under adversarial testing; the one real defect I found is a low-severity overflow at the edge of the supply range, reported with a runnable proof. Nothing outside the test tree was modified, and nothing was installed.\n\n**What I delivered**, all under `test/`, building on the existing 35-test suite without changing it:\n\n- **test/LaunchGuardHookEdge.t.sol**: 23 tests on inputs the implementation did not pick. Re-initialization cannot re-stamp the window. A second PoolManager cannot open a pool through the hook. Swaps on unopened pools fail before the hook, and manager-level calls for unknown pools revert. White-box checks drive beforeSwap and afterSwap as the manager with crafted deltas, including the quote leg at int128 max, int256 max requests, zero and negative outputs, and sells of any size. Partial fills show afterSwap judges realized output while beforeSwap judges the request. A batched router proves the cap is per swap and that one bad swap fails the whole unlock. Tiny supplies round the cap down to one or zero, a mintable token's cap stays frozen, dynamic-fee pools stay capped, liquidity and donations stay open, and the callbacks stay under the gas target. Three fuzz tests at 512 runs cover the full int128 and int256 domains plus any supply.\n- **test/invariant/LaunchGuardHook.invariant.t.sol** with its handler: 128 runs by depth 64 of random buys, sells, liquidity changes, warps across the window, new pools opened mid-run, stranger callbacks and re-initialization attempts. Each swap is first run in a state snapshot with every window closed, so the pool's own answer is the oracle. The real result must equal it exactly, or be a cap rejection naming that exact amount. Eight invariants cover no buy above the cap while active, no rejection outside the window or on sells, immutable launch records, guard state equal to the clock and never reopening, the hook holding no value, and supply conservation.\n- **test/invariant/LaunchToken.invariant.t.sol** with its handler: 256 runs over transfers, overdrawn transfers, allowances, zero-address transfers and admin selectors. Supply is fixed and equals the sum of holder balances.\n- **test/utils/** and **test/mocks/MultiSwapRouter.sol**: shared fixture, a wrapped-error decoder, and a router that runs several swaps in one unlock.\n\n**Findings** are in `.imd-findings.json`, five in total:\n\n- **Low, with proof**: computing the cap as supply times 100 overflows for any token whose supply exceeds about 1.16e75, so such a token can never open a pool. The proof test fails on the current code with a wrapped arithmetic panic and passes once the cap is computed as supply divided by 100.\n- **Info**: a pool initialized at timestamp zero is permanently unswappable because zero is also the \"never initialized\" sentinel. Unreachable on a live chain.\n- **Info**: the cap is per swap, so a router can buy the cap several times in one transaction. This matches the brief and the README, and the batched-swap test documents it.\n- **Info**: the guard only covers pools that name the hook. The token is unrestricted, so a hookless pool or any other venue trades it freely during the window. The README does not state this.\n- **Info**: exact-output requests above the cap are refused even when the price limit would have delivered less. Safe direction, but integrators should size requests to the cap.\n\n**Verification**: `forge build` is clean and `forge test` passes 63 of 63 delivered tests. The only failing test in the tree is the proof under `test/scratch/`, which is supposed to fail and is deleted before submission. The two harness problems I hit along the way were mine, not the hook's: an expected revert consumed by a view call in an argument, and v4's checked liquidity router asserting on fee credits, which I replaced with the no-checks router. The findings file is listed in the repository's git exclude file, so it does not appear in git status; nothing is committed, since the task did not ask ","treeHash":"c7ac781565b1ac66c5c5a69ffbf8173aa218de6c","usage":{"cachedInputTokens":4359757,"inputTokens":834,"model":"claude-fable-5-1","outputTokens":89058,"runtime":"claude","turns":68,"wallClockMs":2267211}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"aad1250d716d3f82","findings":[],"hash":"7d3ee56090a801bdf5b642d8727b18ae18eb915a6600dd301373094d18d05f47","nodeId":"6204ea5b-88ff-4f41-aaab-fcbe26ddf075","outcome":"completed","summary":"Saved [.imd-findings.json](/home/seat/.identitymd/work/486c9374-f896-4d17-b8a0-2a901a6f355f/6204ea5b-88ff-4f41-aaab-fcbe26ddf075/.imd-findings.json).\n\nNo substantiated defects found. All four listed entry points covered; 35 existing tests and six additional control-flow tests passed. Live deployment checks were not run.","treeHash":null,"usage":{"cachedInputTokens":1000192,"inputTokens":96530,"model":"gpt-6-astra","outputTokens":9015,"runtime":"codex","turns":5,"wallClockMs":361072}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"30a6c1a419ef4f9c","findings":[{"citation":"resolved","description":"The guard checks only the amount of the current swap (`params.amountSpecified` in beforeSwap, the realized `delta` in afterSwap). It keeps no running total per pool, per block, per transaction or per sender, so the only invariant it enforces is `bought_in_this_swap <= totalSupply/100`. The purpose stated in the brief and README (protect the first 24 hours of the launch from a large buy) is not reached: anybody can open one PoolManager.unlock and issue N swaps of exactly maxBuy each; every swap passes both callbacks and the batch settles once at the end. Chunked swaps have the same cumulative price impact as one large swap, so the attacker pays nothing extra beyond ~30k gas per extra chunk. Economics from the reproduction: with 30% of supply in a full-range pool at 1:1, a single transaction one block after initialization buys 20% of total supply (2e26 minor units) for ~6.02e26 quote and 597,052 gas; the same amount asked for in one swap is refused by beforeSwap. The README (lines 151-152) says the cap 'slows a single-transaction sweep of the pool'; it does not, since the sweep is still single-transaction. A hookless pool cannot be guarded, but this bypass is inside the guarded pool itself. The literal brief wording ('any swap that buys more than 1%') is satisfied, so this is a design/scope decision for the author: a cumulative cap per pool over the window, or per (pool, sender) / per block, would close it; a cumulative pool-wide cap also caps the whole launch at 1% of supply until the window ends, so the intended protection needs to be chosen explicitly. Reported as medium: a broken guarantee rather than loss of funds, and the author documented the limitation.","line":191,"path":"src/LaunchGuardHook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {PoolManager} from \"v4-core/src/PoolManager.sol\";\nimport {IPoolManager} from \"v4-core/src/interfaces/IPoolManager.sol\";\nimport {IUnlockCallback} from \"v4-core/src/interfaces/callback/IUnlockCallback.sol\";\nimport {IHooks} from \"v4-core/src/interfaces/IHooks.sol\";\nimport {IERC20Minimal} from \"v4-core/src/interfaces/external/IERC20Minimal.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 {BalanceDelta} from \"v4-core/src/types/BalanceDelta.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 {LaunchGuardHook} from \"src/LaunchGuardHook.sol\";\nimport {LaunchToken} from \"src/LaunchToken.sol\";\nimport {HookFlags} from \"src/HookFlags.sol\";\nimport {HookMiner} from \"src/HookMiner.sol\";\n\n/// @dev Plain ERC-20 quote asset with a public mint, defined here so this file imports nothing from test/.\ncontract Quote {\n    string public constant name = \"Quote\";\n    string public constant symbol = \"Q\";\n    uint8 public constant decimals = 18;\n    uint256 public totalSupply;\n    mapping(address => uint256) public balanceOf;\n    mapping(address => mapping(address => uint256)) public allowance;\n\n    function mint(address to, uint256 amount) external {\n        totalSupply += amount;\n        balanceOf[to] += amount;\n    }\n\n    function approve(address spender, uint256 amount) external returns (bool) {\n        allowance[msg.sender][spender] = amount;\n        return true;\n    }\n\n    function transfer(address to, uint256 amount) external returns (bool) {\n        balanceOf[msg.sender] -= amount;\n        balanceOf[to] += amount;\n        return true;\n    }\n\n    function transferFrom(address from, address to, uint256 amount) external returns (bool) {\n        uint256 a = allowance[from][msg.sender];\n        if (a != type(uint256).max) allowance[from][msg.sender] = a - amount;\n        balanceOf[from] -= amount;\n        balanceOf[to] += amount;\n        return true;\n    }\n}\n\n/// @dev One unlock, many swaps. Each swap asks for exactly the per-swap cap, so every one of them\n/// passes the hook, and the whole batch settles once at the end.\ncontract Sweeper is IUnlockCallback {\n    IPoolManager immutable manager;\n\n    constructor(IPoolManager _manager) {\n        manager = _manager;\n    }\n\n    function sweep(PoolKey memory key, bool zeroForOne, uint256 chunk, uint256 n) external returns (uint256 bought) {\n        return abi.decode(manager.unlock(abi.encode(key, zeroForOne, chunk, n)), (uint256));\n    }\n\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        require(msg.sender == address(manager));\n        (PoolKey memory key, bool zeroForOne, uint256 chunk, uint256 n) =\n            abi.decode(data, (PoolKey, bool, uint256, uint256));\n        uint160 limit = zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1;\n        int256 total0;\n        int256 total1;\n        for (uint256 i = 0; i < n; i++) {\n            // Exact-output buy of `chunk` launch tokens: exactly the cap, so beforeSwap and afterSwap both pass.\n            BalanceDelta d = manager.swap(key, SwapParams(zeroForOne, int256(chunk), limit), \"\");\n            total0 += d.amount0();\n            total1 += d.amount1();\n        }\n        uint256 bought = _settleOne(key.currency0, total0) + _settleOne(key.currency1, total1);\n        return abi.encode(bought);\n    }\n\n    function _settleOne(Currency c, int256 amount) internal returns (uint256 received) {\n        if (amount < 0) {\n            manager.sync(c);\n            IERC20Minimal(Currency.unwrap(c)).transfer(address(manager), uint256(-amount));\n            manager.settle();\n        } else if (amount > 0) {\n            manager.take(c, address(this), uint256(amount));\n            received = uint256(amount);\n        }\n    }\n}\n\ncontract ChunkedSweepProofTest is Test {\n    uint160 constant SQRT_PRICE_1_1 = 79228162514264337593543950336;\n    uint160 constant FLAGS = HookFlags.BEFORE_INITIALIZE | HookFlags.BEFORE_SWAP | HookFlags.AFTER_SWAP;\n    int24 constant MIN_TICK = -887_220;\n    int24 constant MAX_TICK = 887_220;\n    uint256 constant SUPPLY = 1e27;\n    uint256 constant MAX_BUY = SUPPLY / 100; // 1% of supply, the hook's per-swap cap\n    uint256 constant CHUNKS = 20; // 20 cap-sized buys = 20% of supply in one transaction\n\n    PoolManager manager;\n    PoolModifyLiquidityTest lpRouter;\n    PoolSwapTest swapRouter;\n    LaunchToken token;\n    Quote quote;\n    LaunchGuardHook hook;\n    Sweeper sweeper;\n    PoolKey key;\n    bool buyDirection;\n\n    function setUp() public {\n        manager = new PoolManager(address(this));\n        lpRouter = new PoolModifyLiquidityTest(manager);\n        swapRouter = new PoolSwapTest(manager);\n        token = new LaunchToken();\n        quote = new Quote();\n        quote.mint(address(this), 1e30);\n\n        bytes memory creationCode =\n            abi.encodePacked(type(LaunchGuardHook).creationCode, abi.encode(manager, address(token)));\n        (, bytes32 salt) = HookMiner.find(address(this), FLAGS, creationCode, 2_000_000);\n        hook = new LaunchGuardHook{salt: salt}(manager, address(token));\n\n        (Currency c0, Currency c1) = address(token) < address(quote)\n            ? (Currency.wrap(address(token)), Currency.wrap(address(quote)))\n            : (Currency.wrap(address(quote)), Currency.wrap(address(token)));\n        key = PoolKey(c0, c1, 3_000, 60, IHooks(address(hook)));\n        // zeroForOne buys currency1, so buying the launch token is zeroForOne iff it is currency1.\n        buyDirection = Currency.unwrap(key.currency1) == address(token);\n\n        manager.initialize(key, SQRT_PRICE_1_1);\n\n        // Seed full-range liquidity: roughly 3e26 of each side at 1:1 (30% of the launch supply in the pool).\n        token.approve(address(lpRouter), type(uint256).max);\n        quote.approve(address(lpRouter), type(uint256).max);\n        lpRouter.modifyLiquidity(key, ModifyLiquidityParams(MIN_TICK, MAX_TICK, 3e26, 0), \"\");\n\n        sweeper = new Sweeper(manager);\n        quote.mint(address(sweeper), 1e30);\n    }\n\n    /// @notice During the window, one swap for 20% of supply is refused, but twenty swaps for 1% each inside\n    /// a single transaction (one `unlock`) are all accepted. The guard must refuse the second as it refuses\n    /// the first; today the sweeper walks away with 20% of the supply, one block after launch.\n    function test_chunkedSweepInsideOneTransactionIsRefusedDuringTheWindow() public {\n        assertTrue(hook.isGuardActive(key.toId()), \"premise: the guard window is open\");\n\n        // Premise: a single buy of the same total is rejected by beforeSwap.\n        quote.approve(address(swapRouter), type(uint256).max);\n        uint160 limit = buyDirection ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1;\n        vm.expectRevert();\n        swapRouter.swap(\n            key,\n            SwapParams(buyDirection, int256(MAX_BUY * CHUNKS), limit),\n            PoolSwapTest.TestSettings({takeClaims: false, settleUsingBurn: false}),\n            \"\"\n        );\n\n        // The same total, split into cap-sized swaps in one unlock, should be refused as well.\n        uint256 before = token.balanceOf(address(sweeper));\n        vm.expectRevert();\n        sweeper.sweep(key, buyDirection, MAX_BUY, CHUNKS);\n        assertEq(token.balanceOf(address(sweeper)), before, \"the sweeper bought launch tokens past the cap\");\n    }\n}","reproduction":"State: LaunchToken (supply 1e27), hook at a 0x20C0 address bound to it, pool (LGRD, quote, fee 3000, ts 60, hook) initialized at T, full-range liquidity 3e26 at price 1:1, block.timestamp < T + 24h. Call 1: PoolSwapTest.swap(key, SwapParams(buyDirection, int256(20 * 1e25), limit)) -> reverts with WrappedError(beforeSwap, BuyExceedsLaunchCap(2e26, 1e25)) as expected. Call 2: a contract calls manager.unlock(...) and inside unlockCallback loops 20 times manager.swap(key, SwapParams(buyDirection, int256(1e25), limit), '') then settles the quote and takes the launch tokens. Expected per the stated guarantee: refused during the window. Actual: succeeds; the caller's LGRD balance grows by 2e26 (20% of total supply, 2000 bps), paying 601,805,416,248,746,238,716,148,466 quote minor units, 597,052 gas, hook.isGuardActive(poolId) still true afterwards. Executed in test/scratch/ChunkedSweep.t.sol (test_showChunkedSweepSucceedsToday passes on this code; test_chunkedSweepInsideOneTransactionIsRefusedDuringTheWindow fails with 'next call did not revert as expected').","severity":"medium","snippet":"        if (capped && params.amountSpecified > 0) {\n            uint256 requested = uint256(params.amountSpecified);\n            if (requested > maxBuy) revert BuyExceedsLaunchCap(requested, maxBuy);","title":"Per-swap cap is defeated by splitting one buy into cap-sized swaps inside a single transaction, so the launch guard gives no economic protection against a sniper"},{"citation":"resolved","description":"The only validation of the pool key is that one currency is the launch token; `key.fee` is not inspected. v4 allows `fee == 0x800000` (DYNAMIC_FEE_FLAG) for any pool with a hook address, and `getInitialLPFee` returns 0 for it. The LP fee of such a pool can only be changed by `PoolManager.updateDynamicLPFee`, which requires `msg.sender == key.hooks`, and this hook never calls it and has no owner. Result: any address can, at any time (including before the official launch), initialize an LGRD pool with this hook that charges LPs 0 fee forever, with the guard window and 1% cap applied to it as if it were a legitimate launch pool. Harm is bounded: LPs who seed that pool earn nothing on volume, and routers will prefer the fee-less pool over the official pool once it has liquidity, diverting trading fees away from the launch's own LPs. The hook cannot be changed after deployment, so this must be decided before launch: either refuse `key.fee.isDynamicFee()` in beforeInitialize, or state that pools other than the manifest pool are out of scope.","line":167,"path":"src/LaunchGuardHook.sol","reproduction":"State: hook deployed and bound to LaunchToken. Call from any EOA: PoolManager.initialize(PoolKey(currency0 = address(0), currency1 = LGRD, fee = 0x800000, tickSpacing = 60, hooks = hook), 79228162514264337593543950336). Expected: refused (the hook has no way to set a dynamic fee). Actual: succeeds; getSlot0 shows sqrtPriceX96 = 2^96, lpFee = 0; hook.isGuardActive(poolId) == true; PoolManager.updateDynamicLPFee(key, 3000) from any address reverts (UnauthorizedDynamicLPFeeUpdate). Executed in test/scratch/DynamicFeePool.t.sol (passes, demonstrating the state).","severity":"low","snippet":"        if (!isCurrency0 && !isCurrency1) revert PoolDoesNotContainLaunchToken();","title":"beforeInitialize accepts the dynamic-fee flag, so anyone can open a launch-token pool through the hook whose LP fee is zero and can never be set"}],"hash":"80060241b00bcddd63e45c498422c9049d672ef56118e823cbef9b4764fce094","nodeId":"d71914f7-7359-4f8c-929b-3a3cc2744c9d","outcome":"completed","summary":"The review is complete and `.imd-findings.json` holds two substantiated findings plus a ten-row coverage record.\n\n**Findings**\n\n- **Medium, `src/LaunchGuardHook.sol:191`.** The cap is enforced only on the current swap, with no running total per pool, sender, block or transaction. Inside one `unlock`, twenty exact-output buys of exactly the cap each pass both callbacks and settle together. In the reproduction, one block after initialization, a single transaction bought 20% of total supply for about 600k gas, while the same amount in one swap was refused. The README's claim that the cap \"slows a single-transaction sweep\" does not hold. The brief's literal per-swap wording is met, so the fix is a scope decision for the author, which the finding spells out. A Foundry proof is attached: it fails on the current code and passes once a cumulative cap exists.\n- **Low, `src/LaunchGuardHook.sol:167`.** The pool key's fee is never validated, so anyone can initialize a launch-token pool through the hook with the dynamic-fee flag. That pool starts at zero LP fee, and since only the hook may call `updateDynamicLPFee` and it never does, the fee is stuck at zero forever. Reproduced with a native-ETH/LGRD pool from an arbitrary address.\n\n**Checked and found holding**\n\n- Window reset: a second `initialize` of the same key reverts in the pool library after the hook write, so the write is rolled back.\n- Per-swap boundary at the cap and at the 24-hour mark, exact-input enforcement in `afterSwap`, sells and post-window swaps untouched.\n- No fund custody, no delta returns, and the aderyn reentrancy lead is a view call to an immutable stock ERC20.\n- All three token entry points are unmodified OpenZeppelin v5.4.0 with fixed supply.\n- Slither timestamp and strict-equality leads are benign sentinel and window comparisons.\n\nNot reached in depth: liquidity-position acquisition as a non-swap path to launch tokens, which I judged legitimate limit-order behavior rather than a cap bypass, and hookless pools, which are inherent to a hook-only guard. Scratch tests live under `test/scratch/`, which is gitignored.","treeHash":null,"usage":{"cachedInputTokens":976736,"inputTokens":290,"model":"claude-fable-5-1","outputTokens":26017,"runtime":"claude","turns":25,"wallClockMs":396618}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"377843575071cdb1","findings":[],"hash":"e16b77f3d0a5b119ea29f6d4a28828aad252c8fcf9740685482089c65ce9c723","nodeId":"b48f75d2-e0ee-419b-8a83-b277ff01d986","outcome":"completed","summary":"Saved [`.imd-findings.json`](/home/worker/.identitymd/work/486c9374-f896-4d17-b8a0-2a901a6f355f/b48f75d2-e0ee-419b-8a83-b277ff01d986/.imd-findings.json) with no substantiated defects and coverage for all four required entry points.\n\nAll 35 existing Foundry tests passed. Production files remain unchanged. Protected harnesses were read; protected-suite and fork execution remain unverified.","treeHash":null,"usage":{"cachedInputTokens":786432,"inputTokens":113080,"model":"gpt-6-astra","outputTokens":6022,"runtime":"codex","turns":5,"wallClockMs":217019}},{"artifacts":[],"attempt":1,"bundleHash":"5a3d5cecc0293bd9c98e8e55a8410d55403e904d683a1cd88d9f9c290d9a32ad","device":"3f6a9bdd601cb99f","findings":[],"hash":"e63116736099bf4e37ce9cefca4381cb2c9316a7451b9cd016cf0485a0abbe67","nodeId":"eeaf1720-a77a-42f5-8d9f-a60eb5afd816","outcome":"completed","summary":"Created [launch.json](/home/seat/.identitymd/work/486c9374-f896-4d17-b8a0-2a901a6f355f/eeaf1720-a77a-42f5-8d9f-a60eb5afd816/launch.json) with matching permissions and native ETH pool settings.\n\nSchema validation, `forge build`, and all 35 tests passed. Only the manifest changed. Constructor addresses are documented as deployment-time references.","treeHash":"f12000e63f04010d8a1ef979cae77691f36850ac","usage":{"cachedInputTokens":319360,"inputTokens":42087,"model":"gpt-6-astra","outputTokens":4292,"runtime":"codex","turns":3,"wallClockMs":155941}}],"verification":[{"checks":[{"durationMs":14268,"exitCode":0,"name":"build","output":"Compiling 85 files with Solc 0.8.26\nSolc 0.8.26 finished in 14.14s\nCompiler run successful!\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/LaunchGuardHook.sol:224:77\n    │\n224 │         if (block.timestamp >= uint256(startedAt) + GUARD_DURATION) return (false, 0);\n    │                                                                             ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/LaunchGuardHook.sol:228:39\n    │\n228 │         if (!buysLaunchToken) return (false, 0);\n    │                                       ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/LaunchGuardHook.sol:229:17\n    │\n229 │         return (true, launch.maxBuy);\n    │                 ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LaunchGuardHook.sol:145:34\n    │\n145 │         return startedAt != 0 && block.timestamp < uint256(startedAt) + GUARD_DURATION;\n    │                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LaunchGuardHook.sol:174:28\n    │\n174 │         uint64 startedAt = uint64(block.timestamp);\n    │                            ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LaunchGuardHook.sol:177:64\n    │\n177 │         emit LaunchGuardStarted(poolId, startedAt, startedAt + uint64(GUARD_DURATION), isCurrency0, maxBuy);\n    │                                                                ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LaunchGuardHook.sol:192:33\n    │\n192 │             uint256 requested = uint256(params.amountSpecified);\n    │                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LaunchGuardHook.sol:212:34\n    │\n212 │                 uint256 bought = uint256(uint128(received));\n    │                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LaunchGuardHook.sol:212:42\n    │\n212 │                 uint256 bought = uint256(uint128(received));\n    │                                          ━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LaunchGuardHook.sol:224:13\n    │\n224 │         if (block.timestamp >= uint256(startedAt) + GUARD_DURATION) return (false, 0);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\n","passed":true},{"durationMs":167,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 3 tests for test/DeployLaunch.t.sol:DeployLaunchTest\n[PASS] test_deployProducesBoundTokenAndHookAtFlaggedAddress() (gas: 2048066)\n[PASS] test_deployRejectsManagerWithoutCode() (gas: 58679)\n[PASS] test_mineSaltIsDeterministicAndMatchesDeployment() (gas: 2381680)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 2.13ms (2.57ms CPU time)\n\nRan 6 tests for test/LaunchToken.t.sol:LaunchTokenTest\n[PASS] test_metadata() (gas: 24431)\n[PASS] test_noMintOrAdminEntryPoints() (gas: 173526)\n[PASS] test_supplyIsExactlyOneBillionTokensMintedToDeployer() (gas: 21609)\n[PASS] test_transferFromRespectsAllowance() (gas: 145760)\n[PASS] test_transferMovesExactlyTheRequestedAmount() (gas: 74102)\n[PASS] test_transferOfMoreThanBalanceReverts() (gas: 34369)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 2.12ms (1.30ms CPU time)\n\nRan 26 tests for test/LaunchGuardHook.t.sol:LaunchGuardHookTest\n[PASS] testFuzz_afterTheWindowAnyAmountPasses(uint256,uint256) (runs: 256, μ: 189747, ~: 188730)\nLogs:\n  Bound result 10\n  Bound result 887220\n\n[PASS] testFuzz_exactInputBuyIsAllowedIffRealizedOutputAtMostTheCap(uint256,bool) (runs: 256, μ: 209983, ~: 226691)\nLogs:\n  Bound result 8464011365928255202459812\n\n[PASS] testFuzz_exactOutputBuyIsAllowedIffAtMostTheCap(uint256,bool) (runs: 256, μ: 181717, ~: 182576)\nLogs:\n  Bound result 18828844133065258602438772\n\n[PASS] test_callbacksRefuseCallersOtherThanThePoolManager() (gas: 96282)\n[PASS] test_capHoldsUntilTheLastSecondOfTheWindow() (gas: 418564)\n[PASS] test_capLiftsExactlyWhenTheWindowCloses() (gas: 534002)\n[PASS] test_constructorRejectsAddressWithoutTheFlags() (gas: 10239)\n[PASS] test_constructorRejectsZeroAddresses() (gas: 6500)\n[PASS] test_exactInputBuyOverTheCapIsRejectedInAfterSwap() (gas: 442083)\n[PASS] test_exactInputBuyUnderTheCapSucceeds() (gas: 355222)\n[PASS] test_exactOutputBuyAtTheCapSucceeds() (gas: 355576)\n[PASS] test_exactOutputBuyOverTheCapIsRejectedInBeforeSwap() (gas: 515730)\n[PASS] test_initializeEmitsAndRoutesThroughBeforeInitialize() (gas: 132197)\n[PASS] test_initializeRecordsTheLaunchForEachPool() (gas: 68996)\n[PASS] test_initializeRejectsLaunchTokenWithNoSupply() (gas: 10240015)\n[PASS] test_initializeRejectsPoolWithoutTheLaunchToken() (gas: 1560792)\n[PASS] test_longAfterTheWindowEverythingIsAllowed() (gas: 347210)\n[PASS] test_nobodyCanInitializeThePoolBeforeTheHookExists() (gas: 9648111)\n[PASS] test_permissionsMatchAddressAndDeclaredFlags() (gas: 29983)\n[PASS] test_rejectedSwapLeavesPoolAndBalancesUntouched() (gas: 440420)\n[PASS] test_repeatedBuysAtTheCapAreEachAllowed() (gas: 493222)\n[PASS] test_sellsAreNeverCappedDuringTheWindow() (gas: 445561)\n[PASS] test_smallExactOutputBuySucceeds() (gas: 354757)\n[PASS] test_unimplementedCallbacksRevert() (gas: 112535)\n[PASS] test_windowIsPerPool() (gas: 928927)\n[PASS] test_worksWithNativeQuoteCurrency() (gas: 630371)\nSuite result: ok. 26 passed; 0 failed; 0 skipped; finished in 79.11ms (107.43ms CPU time)\n\nRan 3 test suites in 79.78ms (83.36ms CPU time): 35 tests passed, 0 failed, 0 skipped (35 total tests)\n","passed":true},{"durationMs":40,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"LaunchGuardHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"LaunchToken.approve(address,uint256)\",\"LaunchToken.transfer(address,uint256)\",\"LaunchToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":11,\"README.md\":206,\"foundry.toml\":33,\"remappings.txt\":5,\"script/DeployLaunch.s.sol\":65,\"src/HookFlags.sol\":59,\"src/HookMiner.sol\":35,\"src/LaunchGuardHook.sol\":305,\"src/LaunchToken.sol\":19,\"test/DeployLaunch.t.sol\":50,\"test/LaunchGuardHook.t.sol\":572,\"test/LaunchToken.t.sol\":80,\"test/mocks/MockERC20.sol\":15},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":1430,"exitCode":0,"name":"slither","output":"[medium/high] incorrect-equality at src/LaunchGuardHook.sol:149: LaunchGuardHook.guardEndsAt(PoolId) (src/LaunchGuardHook.sol#149-152) uses a dangerous strict equality:\n[low/medium] timestamp at src/LaunchGuardHook.sol:149: LaunchGuardHook.guardEndsAt(PoolId) (src/LaunchGuardHook.sol#149-152) uses timestamp for comparisons\n[low/medium] timestamp at src/LaunchGuardHook.sol:220: LaunchGuardHook._activeBuyCap(PoolKey,bool) (src/LaunchGuardHook.sol#220-230) uses timestamp for comparisons\n[low/medium] timestamp at src/LaunchGuardHook.sol:143: LaunchGuardHook.isGuardActive(PoolId) (src/LaunchGuardHook.sol#143-146) uses timestamp for comparisons","passed":true},{"durationMs":358,"exitCode":0,"name":"aderyn","output":"[high] reentrancy-state-change at src/LaunchGuardHook.sol:169: Reentrancy: State change after external call\n[low] internal-function-used-once at src/HookFlags.sol:31: Internal Function Used Only Once (2 places)\n[low] large-numeric-literal at src/LaunchGuardHook.sol:50: Large Numeric Literal (2 places)\n[low] push-zero-opcode at src/HookFlags.sol:2: PUSH0 Opcode (2 places)\n[low] unused-public-function at src/LaunchGuardHook.sol:143: Public Function Not Used Internally","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"2b423287197ab37ba11121eceb8c3146be87c7c0525cb68279c6330796da279f","verifiedTreeHash":"c755c87f1a891d28a5665108518ed71271806e2c","verifierVersion":"0.1.0+9f8ef295"},{"checks":[{"durationMs":14188,"exitCode":0,"name":"build","output":"Compiling 85 files with Solc 0.8.26\nSolc 0.8.26 finished in 14.06s\nCompiler run successful!\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/LaunchGuardHook.sol:224:77\n    │\n224 │         if (block.timestamp >= uint256(startedAt) + GUARD_DURATION) return (false, 0);\n    │                                                                             ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/LaunchGuardHook.sol:228:39\n    │\n228 │         if (!buysLaunchToken) return (false, 0);\n    │                                       ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/LaunchGuardHook.sol:229:17\n    │\n229 │         return (true, launch.maxBuy);\n    │                 ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LaunchGuardHook.sol:145:34\n    │\n145 │         return startedAt != 0 && block.timestamp < uint256(startedAt) + GUARD_DURATION;\n    │                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LaunchGuardHook.sol:174:28\n    │\n174 │         uint64 startedAt = uint64(block.timestamp);\n    │                            ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LaunchGuardHook.sol:177:64\n    │\n177 │         emit LaunchGuardStarted(poolId, startedAt, startedAt + uint64(GUARD_DURATION), isCurrency0, maxBuy);\n    │                                                                ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LaunchGuardHook.sol:192:33\n    │\n192 │             uint256 requested = uint256(params.amountSpecified);\n    │                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LaunchGuardHook.sol:212:34\n    │\n212 │                 uint256 bought = uint256(uint128(received));\n    │                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LaunchGuardHook.sol:212:42\n    │\n212 │                 uint256 bought = uint256(uint128(received));\n    │                                          ━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LaunchGuardHook.sol:224:13\n    │\n224 │         if (block.timestamp >= uint256(startedAt) + GUARD_DURATION) return (false, 0);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\n","passed":true},{"durationMs":159,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 3 tests for test/DeployLaunch.t.sol:DeployLaunchTest\n[PASS] test_deployProducesBoundTokenAndHookAtFlaggedAddress() (gas: 2048066)\n[PASS] test_deployRejectsManagerWithoutCode() (gas: 58679)\n[PASS] test_mineSaltIsDeterministicAndMatchesDeployment() (gas: 2381680)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 2.13ms (2.53ms CPU time)\n\nRan 6 tests for test/LaunchToken.t.sol:LaunchTokenTest\n[PASS] test_metadata() (gas: 24431)\n[PASS] test_noMintOrAdminEntryPoints() (gas: 173526)\n[PASS] test_supplyIsExactlyOneBillionTokensMintedToDeployer() (gas: 21609)\n[PASS] test_transferFromRespectsAllowance() (gas: 145760)\n[PASS] test_transferMovesExactlyTheRequestedAmount() (gas: 74102)\n[PASS] test_transferOfMoreThanBalanceReverts() (gas: 34369)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 2.14ms (1.16ms CPU time)\n\nRan 26 tests for test/LaunchGuardHook.t.sol:LaunchGuardHookTest\n[PASS] testFuzz_afterTheWindowAnyAmountPasses(uint256,uint256) (runs: 256, μ: 189653, ~: 188715)\nLogs:\n  Bound result 2048\n  Bound result 315290803\n\n[PASS] testFuzz_exactInputBuyIsAllowedIffRealizedOutputAtMostTheCap(uint256,bool) (runs: 256, μ: 217826, ~: 227435)\nLogs:\n  Bound result 50000000000000000000000000\n\n[PASS] testFuzz_exactOutputBuyIsAllowedIffAtMostTheCap(uint256,bool) (runs: 256, μ: 181818, ~: 182564)\nLogs:\n  Bound result 3046054117791\n\n[PASS] test_callbacksRefuseCallersOtherThanThePoolManager() (gas: 96282)\n[PASS] test_capHoldsUntilTheLastSecondOfTheWindow() (gas: 418564)\n[PASS] test_capLiftsExactlyWhenTheWindowCloses() (gas: 534002)\n[PASS] test_constructorRejectsAddressWithoutTheFlags() (gas: 10239)\n[PASS] test_constructorRejectsZeroAddresses() (gas: 6500)\n[PASS] test_exactInputBuyOverTheCapIsRejectedInAfterSwap() (gas: 442083)\n[PASS] test_exactInputBuyUnderTheCapSucceeds() (gas: 355222)\n[PASS] test_exactOutputBuyAtTheCapSucceeds() (gas: 355576)\n[PASS] test_exactOutputBuyOverTheCapIsRejectedInBeforeSwap() (gas: 515730)\n[PASS] test_initializeEmitsAndRoutesThroughBeforeInitialize() (gas: 132197)\n[PASS] test_initializeRecordsTheLaunchForEachPool() (gas: 68996)\n[PASS] test_initializeRejectsLaunchTokenWithNoSupply() (gas: 10240015)\n[PASS] test_initializeRejectsPoolWithoutTheLaunchToken() (gas: 1560792)\n[PASS] test_longAfterTheWindowEverythingIsAllowed() (gas: 347210)\n[PASS] test_nobodyCanInitializeThePoolBeforeTheHookExists() (gas: 9648111)\n[PASS] test_permissionsMatchAddressAndDeclaredFlags() (gas: 29983)\n[PASS] test_rejectedSwapLeavesPoolAndBalancesUntouched() (gas: 440420)\n[PASS] test_repeatedBuysAtTheCapAreEachAllowed() (gas: 493222)\n[PASS] test_sellsAreNeverCappedDuringTheWindow() (gas: 445561)\n[PASS] test_smallExactOutputBuySucceeds() (gas: 354757)\n[PASS] test_unimplementedCallbacksRevert() (gas: 112535)\n[PASS] test_windowIsPerPool() (gas: 928927)\n[PASS] test_worksWithNativeQuoteCurrency() (gas: 630371)\nSuite result: ok. 26 passed; 0 failed; 0 skipped; finished in 75.95ms (102.63ms CPU time)\n\nRan 3 test suites in 76.86ms (80.23ms CPU time): 35 tests passed, 0 failed, 0 skipped (35 total tests)\n","passed":true},{"durationMs":42,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"LaunchGuardHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"LaunchToken.approve(address,uint256)\",\"LaunchToken.transfer(address,uint256)\",\"LaunchToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":11,\"README.md\":206,\"foundry.toml\":33,\"launch.json\":28,\"remappings.txt\":5,\"script/DeployLaunch.s.sol\":65,\"src/HookFlags.sol\":59,\"src/HookMiner.sol\":35,\"src/LaunchGuardHook.sol\":305,\"src/LaunchToken.sol\":19,\"test/DeployLaunch.t.sol\":50,\"test/LaunchGuardHook.t.sol\":572,\"test/LaunchToken.t.sol\":80,\"test/mocks/MockERC20.sol\":15},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"3867a9445dc904f9cd59b0cc984099710b11184f12aeb5fb57cf8186630fc157","verifiedTreeHash":"cdc2b60cd34d5310f4e47afc79ede9b9148b9b61","verifierVersion":"0.1.0+9f8ef295"},{"checks":[{"durationMs":41352,"exitCode":0,"name":"build","output":"Compiling 96 files with Solc 0.8.26\nSolc 0.8.26 finished in 41.19s\nCompiler run successful!\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/LaunchGuardHook.sol:224:77\n    │\n224 │         if (block.timestamp >= uint256(startedAt) + GUARD_DURATION) return (false, 0);\n    │                                                                             ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/LaunchGuardHook.sol:228:39\n    │\n228 │         if (!buysLaunchToken) return (false, 0);\n    │                                       ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/LaunchGuardHook.sol:229:17\n    │\n229 │         return (true, launch.maxBuy);\n    │                 ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LaunchGuardHook.sol:145:34\n    │\n145 │         return startedAt != 0 && block.timestamp < uint256(startedAt) + GUARD_DURATION;\n    │                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LaunchGuardHook.sol:174:28\n    │\n174 │         uint64 startedAt = uint64(block.timestamp);\n    │                            ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LaunchGuardHook.sol:177:64\n    │\n177 │         emit LaunchGuardStarted(poolId, startedAt, startedAt + uint64(GUARD_DURATION), isCurrency0, maxBuy);\n    │                                                                ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LaunchGuardHook.sol:192:33\n    │\n192 │             uint256 requested = uint256(params.amountSpecified);\n    │                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LaunchGuardHook.sol:212:34\n    │\n212 │                 uint256 bought = uint256(uint128(received));\n    │                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LaunchGuardHook.sol:212:42\n    │\n212 │                 uint256 bought = uint256(uint128(received));\n    │                                          ━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LaunchGuardHook.sol:224:13\n    │\n224 │         if (block.timestamp >= uint256(startedAt) + GUARD_DURATION) return (false, 0);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\n","passed":true},{"durationMs":9024,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 6 tests for test/LaunchToken.t.sol:LaunchTokenTest\n[PASS] test_metadata() (gas: 24431)\n[PASS] test_noMintOrAdminEntryPoints() (gas: 173526)\n[PASS] test_supplyIsExactlyOneBillionTokensMintedToDeployer() (gas: 21609)\n[PASS] test_transferFromRespectsAllowance() (gas: 145760)\n[PASS] test_transferMovesExactlyTheRequestedAmount() (gas: 74102)\n[PASS] test_transferOfMoreThanBalanceReverts() (gas: 34369)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 2.21ms (1.10ms CPU time)\n\nRan 3 tests for test/DeployLaunch.t.sol:DeployLaunchTest\n[PASS] test_deployProducesBoundTokenAndHookAtFlaggedAddress() (gas: 2048066)\n[PASS] test_deployRejectsManagerWithoutCode() (gas: 58679)\n[PASS] test_mineSaltIsDeterministicAndMatchesDeployment() (gas: 2381680)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 2.26ms (2.54ms CPU time)\n\nRan 26 tests for test/LaunchGuardHook.t.sol:LaunchGuardHookTest\n[PASS] testFuzz_afterTheWindowAnyAmountPasses(uint256,uint256) (runs: 256, μ: 189589, ~: 188712)\nLogs:\n  Bound result 72648338429398245487179572\n  Bound result 193531945\n\n[PASS] testFuzz_exactInputBuyIsAllowedIffRealizedOutputAtMostTheCap(uint256,bool) (runs: 256, μ: 216106, ~: 227431)\nLogs:\n  Bound result 49999999000000000000003648\n\n[PASS] testFuzz_exactOutputBuyIsAllowedIffAtMostTheCap(uint256,bool) (runs: 256, μ: 181702, ~: 182564)\nLogs:\n  Bound result 3647\n\n[PASS] test_callbacksRefuseCallersOtherThanThePoolManager() (gas: 96282)\n[PASS] test_capHoldsUntilTheLastSecondOfTheWindow() (gas: 418564)\n[PASS] test_capLiftsExactlyWhenTheWindowCloses() (gas: 534002)\n[PASS] test_constructorRejectsAddressWithoutTheFlags() (gas: 10239)\n[PASS] test_constructorRejectsZeroAddresses() (gas: 6500)\n[PASS] test_exactInputBuyOverTheCapIsRejectedInAfterSwap() (gas: 442083)\n[PASS] test_exactInputBuyUnderTheCapSucceeds() (gas: 355222)\n[PASS] test_exactOutputBuyAtTheCapSucceeds() (gas: 355576)\n[PASS] test_exactOutputBuyOverTheCapIsRejectedInBeforeSwap() (gas: 515730)\n[PASS] test_initializeEmitsAndRoutesThroughBeforeInitialize() (gas: 132197)\n[PASS] test_initializeRecordsTheLaunchForEachPool() (gas: 68996)\n[PASS] test_initializeRejectsLaunchTokenWithNoSupply() (gas: 10240015)\n[PASS] test_initializeRejectsPoolWithoutTheLaunchToken() (gas: 1560792)\n[PASS] test_longAfterTheWindowEverythingIsAllowed() (gas: 347210)\n[PASS] test_nobodyCanInitializeThePoolBeforeTheHookExists() (gas: 9648111)\n[PASS] test_permissionsMatchAddressAndDeclaredFlags() (gas: 29983)\n[PASS] test_rejectedSwapLeavesPoolAndBalancesUntouched() (gas: 440420)\n[PASS] test_repeatedBuysAtTheCapAreEachAllowed() (gas: 493222)\n[PASS] test_sellsAreNeverCappedDuringTheWindow() (gas: 445561)\n[PASS] test_smallExactOutputBuySucceeds() (gas: 354757)\n[PASS] test_unimplementedCallbacksRevert() (gas: 112535)\n[PASS] test_windowIsPerPool() (gas: 928927)\n[PASS] test_worksWithNativeQuoteCurrency() (gas: 630371)\nSuite result: ok. 26 passed; 0 failed; 0 skipped; finished in 839.73ms (221.22ms CPU time)\n\nRan 23 tests for test/LaunchGuardHookEdge.t.sol:LaunchGuardHookEdgeTest\n[PASS] testFuzz_afterSwapRejectsExactlyBuysOverTheCapWhileActive(int128,bool,uint256) (runs: 512, μ: 31660, ~: 32589)\nLogs:\n  Bound result 169988\n\n[PASS] testFuzz_beforeSwapRejectsExactlyExactOutputBuysOverTheCapWhileActive(int256,bool,uint256) (runs: 512, μ: 30830, ~: 31465)\nLogs:\n  Bound result 2213\n\n[PASS] testFuzz_capIsOnePercentOfTheSupplyAtInitialization(uint256) (runs: 512, μ: 10248506, ~: 10250275)\nLogs:\n  Bound result 1907\n\n[PASS] test_aForeignPoolManagerCannotOpenAPoolThroughTheHook() (gas: 5334930)\n[PASS] test_afterSwapBoundaryIsInclusiveAndReadsTheLaunchLeg() (gas: 171788)\n[PASS] test_afterSwapIgnoresSellsOfAnySize() (gas: 37634)\n[PASS] test_anotherRouterIsGuardedTheSameWay() (gas: 1115669)\n[PASS] test_batchedSwapsInOneUnlockAreEachCappedAndTheBatchFailsAsAWhole() (gas: 1383136)\n[PASS] test_beforeSwapCannotJudgeExactInputBuysAndDefersToAfterSwap() (gas: 41165)\n[PASS] test_beforeSwapExactOutputBoundaryIsInclusive() (gas: 103757)\n[PASS] test_beforeSwapIgnoresSellsOfAnySize() (gas: 37213)\n[PASS] test_capIsFrozenAtTheSupplyReadDuringInitialization() (gas: 10314658)\n[PASS] test_capRoundsDownForSuppliesThatAreNotMultiplesOfOneHundred() (gas: 10282729)\n[PASS] test_dynamicFeePoolOpensThroughTheHookAndStaysCapped() (gas: 621938)\n[PASS] test_exactInputBuyWithATightPriceLimitIsJudgedOnRealizedOutput() (gas: 355656)\n[PASS] test_exactOutputBuyAtTheCapShortenedByThePriceLimitSucceeds() (gas: 179774)\n[PASS] test_exactOutputRequestOverTheCapIsRejectedBeforeThePriceLimitCanShortenIt() (gas: 162028)\n[PASS] test_liquidityAndDonationsAreNotRestrictedDuringTheWindow() (gas: 1694412)\n[PASS] test_managerCallbacksOnAPoolTheHookNeverSawRevertUnknownPool() (gas: 34760)\n[PASS] test_reinitializingThePoolCannotResetTheWindow() (gas: 124111)\n[PASS] test_supplyBelowOneHundredUnitsMeansNoBuyAtAllDuringTheWindow() (gas: 10299978)\n[PASS] test_swapCallbacksStayFarUnderTheGasTarget() (gas: 42872)\n[PASS] test_swapOnAnUninitializedPoolFailsBeforeReachingTheHook() (gas: 77625)\nSuite result: ok. 23 passed; 0 failed; 0 skipped; finished in 839.72ms (968.81ms CPU time)\n\nRan 1 test for test/invariant/LaunchToken.invariant.t.sol:LaunchTokenInvariantTest\n[PASS]\nLaunchTokenInvariantTest invariants:\n[PASS] invariant_noAdminEntryPointAnswers\n[PASS] invariant_supplyEqualsSumOfHolderBalances\n[PASS] invariant_supplyIsFixed\n LaunchTokenInvariantTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭--------------------+------------------------+-------+---------+----------╮\n| Contract           | Selector               | Calls | Reverts | Discards |\n+==========================================================================+\n| LaunchTokenHandler | adminCall              | 4061  | 0       | 0        |\n|--------------------+------------------------+-------+---------+----------|\n| LaunchTokenHandler | approveAndTransferFrom | 4160  | 0       | 0        |\n|--------------------+------------------------+-------+---------+----------|\n| LaunchTokenHandler | transfer               | 4059  | 0       | 0        |\n|--------------------+------------------------+-------+---------+----------|\n| LaunchTokenHandler | transferToZero         | 4104  | 0       | 0        |\n╰--------------------+------------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 0\n  Bound result 1\n  Bound result 0\n  Bound result 11\n  Bound result 123456000000000000000000\n  Bound result 128\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 1\n  Bound result 200000000000000000000000000\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 10000000000000000000\n  Bound result 1\n  Bound result 0\n  Bound result 6\n  Bound result 462\n  Bound result 68\n  Bound result 4068\n  Bound result 4876\n  Bound result 702631940928741196493854\n  Bound result 0\n  Bound result 61\n  Bound result 12669\n  Bound result 123544572953458\n  Bound result 200000000000000000000004067\n  Bound result 10814\n  Bound result 7\n  Bound result 5567\n  Bound result 0\n  Bound result 1341\n  Bound result 1\n  Bound result 174831835401248082107944568\n  Bound result 3808\n  Bound result 1673\n  Bound result 2025\n  Bound result 30000\n  Bound result 0\n  Bound result 2100\n  Bound result 14\n  Bound result 0\n  Bound result 1\n  Bound result 0\n  Bound result 3995\n  Bound result 2354\n  Bound result 0\n  Bound result 116910115000565378822881449\n  Bound result 259200\n  Bound result 11239\n  Bound result 2008\n  Bound result 5\n  Bound result 200\n  Bound result 0\n  Bound result 123456000000000000000000\n  Bound result 1642\n  Bound result 1951\n  Bound result 3089\n  Bound result 1\n  Bound result 1\n  Bound result 1860\n  Bound result 122400\n  Bound result 5370\n  Bound result 600\n  Bound result 744\n  Bound result 13\n  Bound result 8\n  Bound result 1365\n  Bound result 2253\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.64s (1.63s CPU time)\n\nRan 1 test for test/invariant/LaunchGuardHook.invariant.t.sol:LaunchGuardHookInvariantTest\n[PASS]\nLaunchGuardHookInvariantTest invariants:\n[PASS] invariant_capOnlyFiresOnBuysInsideTheWindow\n[PASS] invariant_guardFollowsTheClockAndNeverReopens\n[PASS] invariant_guardNeverAltersAnAcceptedSwap\n[PASS] invariant_hookHoldsNoValue\n[PASS] invariant_launchRecordsAreImmutable\n[PASS] invariant_launchSupplyIsConserved\n[PASS] invariant_noBuyAboveTheCapWhileTheGuardIsActive\n[PASS] invariant_onlyTheManagerDrivesTheHook\n LaunchGuardHookInvariantTest invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭--------------------+------------------+-------+---------+----------╮\n| Contract           | Selector         | Calls | Reverts | Discards |\n+====================================================================+\n| LaunchGuardHandler | addLiquidity     | 935   | 0       | 0        |\n|--------------------+------------------+-------+---------+----------|\n| LaunchGuardHandler | exactInputBuy    | 897   | 0       | 0        |\n|--------------------+------------------+-------+---------+----------|\n| LaunchGuardHandler | exactOutputBuy   | 887   | 0       | 0        |\n|--------------------+------------------+-------+---------+----------|\n| LaunchGuardHandler | openPool         | 904   | 0       | 0        |\n|--------------------+------------------+-------+---------+----------|\n| LaunchGuardHandler | reinitialize     | 882   | 0       | 0        |\n|--------------------+------------------+-------+---------+----------|\n| LaunchGuardHandler | removeLiquidity  | 937   | 0       | 0        |\n|--------------------+------------------+-------+---------+----------|\n| LaunchGuardHandler | sell             | 932   | 0       | 0        |\n|--------------------+------------------+-------+---------+----------|\n| LaunchGuardHandler | strangerCallback | 930   | 0       | 0        |\n|--------------------+------------------+-------+---------+----------|\n| LaunchGuardHandler | warp             | 888   | 0       | 0        |\n╰--------------------+------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 150\n  Bound result 27379\n  Bound result 10000000000000000000000000\n  Bound result 2568\n  Bound result 123456000000000000000000\n  Bound result 1868557048690904721154856\n  Bound result 7515448276879390525507510\n  Bound result 22110\n  Bound result 9878012218923115954044928\n  Bound result 36000\n  Bound result 1015\n  Bound result 2685322247616032409381256\n  Bound result 2788\n  Bound result 9999999000000000000003001\n  Bound result 6393750760538294120492199\n  Bound result 38463463374607431768211455\n  Bound result 14527403830737223597162495\n  Bound result 5\n  Bound result 2048\n  Bound result 9999999000000000000000007\n  Bound result 50000\n  Bound result 5916\n  Bound result 9999999000000000000002049\n  Bound result 15516834207367116967366995\n  Bound result 8439\n  Bound result 4523898595700623933814770\n  Bound result 5000\n  Bound result 49999999999000000007431176\n  Bound result 50000\n  Bound result 4984\n  Bound result 1\n  Bound result 32840196159177102376611515\n  Bound result 49999999999000000000000100\n  Bound result 27287670506578077774511672\n  Bound result 5000\n  Bound result 1024\n  Bound result 1886\n  Bound result 17674769463645892717534\n  Bound result 45436\n  Bound result 14832\n  Bound result 9999999000000000000004097\n  Bound result 16165\n  Bound result 438\n  Bound result 27822113866227810375002386\n  Bound result 37292\n  Bound result 693\n  accepted buys while active    11\n  accepted buys while inactive  1\n  cap rejections                4\n    in beforeSwap / afterSwap   2 2\n  other reverts                 1\n  sells                         8\n  pools opened                  4\n  stranger callbacks refused    5\n  reinitializations refused     4\n  liquidity adds / removes      9 6\n  liquidity reverts             0\n  largest buy while active      10000000000000000000000000\n  largest buy while inactive    693\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 8.91s (8.91s CPU time)\n\nRan 6 test suites in 8.92s (12.23s CPU time): 60 tests passed, 0 failed, 0 skipped (60 total tests)\n","passed":true},{"durationMs":76,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"LaunchGuardHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"LaunchToken.approve(address,uint256)\",\"LaunchToken.transfer(address,uint256)\",\"LaunchToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":11,\"README.md\":206,\"foundry.toml\":33,\"remappings.txt\":5,\"script/DeployLaunch.s.sol\":65,\"src/HookFlags.sol\":59,\"src/HookMiner.sol\":35,\"src/LaunchGuardHook.sol\":305,\"src/LaunchToken.sol\":19,\"test/DeployLaunch.t.sol\":50,\"test/LaunchGuardHook.t.sol\":572,\"test/LaunchGuardHookEdge.t.sol\":540,\"test/LaunchToken.t.sol\":80,\"test/invariant/LaunchGuardHandler.sol\":378,\"test/invariant/LaunchGuardHook.invariant.t.sol\":151,\"test/invariant/LaunchToken.invariant.t.sol\":51,\"test/invariant/LaunchTokenHandler.sol\":130,\"test/mocks/MockERC20.sol\":15,\"test/mocks/MultiSwapRouter.sol\":62,\"test/utils/HookErrors.sol\":64,\"test/utils/LaunchFixture.sol\":140},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"5f515228d633989050f902541c6162cd22d06c87379edcbbd4f43edb097b0721","verifiedTreeHash":"c7ac781565b1ac66c5c5a69ffbf8173aa218de6c","verifierVersion":"0.1.0+da6bdbe5"},{"checks":[{"durationMs":17192,"exitCode":0,"name":"build","output":"Compiling 85 files with Solc 0.8.26\nSolc 0.8.26 finished in 17.04s\nCompiler run successful!\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/LaunchGuardHook.sol:224:77\n    │\n224 │         if (block.timestamp >= uint256(startedAt) + GUARD_DURATION) return (false, 0);\n    │                                                                             ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/LaunchGuardHook.sol:228:39\n    │\n228 │         if (!buysLaunchToken) return (false, 0);\n    │                                       ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/LaunchGuardHook.sol:229:17\n    │\n229 │         return (true, launch.maxBuy);\n    │                 ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LaunchGuardHook.sol:145:34\n    │\n145 │         return startedAt != 0 && block.timestamp < uint256(startedAt) + GUARD_DURATION;\n    │                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LaunchGuardHook.sol:174:28\n    │\n174 │         uint64 startedAt = uint64(block.timestamp);\n    │                            ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LaunchGuardHook.sol:177:64\n    │\n177 │         emit LaunchGuardStarted(poolId, startedAt, startedAt + uint64(GUARD_DURATION), isCurrency0, maxBuy);\n    │                                                                ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LaunchGuardHook.sol:192:33\n    │\n192 │             uint256 requested = uint256(params.amountSpecified);\n    │                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LaunchGuardHook.sol:212:34\n    │\n212 │                 uint256 bought = uint256(uint128(received));\n    │                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LaunchGuardHook.sol:212:42\n    │\n212 │                 uint256 bought = uint256(uint128(received));\n    │                                          ━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LaunchGuardHook.sol:224:13\n    │\n224 │         if (block.timestamp >= uint256(startedAt) + GUARD_DURATION) return (false, 0);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\n","passed":true},{"durationMs":221,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 6 tests for test/LaunchToken.t.sol:LaunchTokenTest\n[PASS] test_metadata() (gas: 24431)\n[PASS] test_noMintOrAdminEntryPoints() (gas: 173526)\n[PASS] test_supplyIsExactlyOneBillionTokensMintedToDeployer() (gas: 21609)\n[PASS] test_transferFromRespectsAllowance() (gas: 145760)\n[PASS] test_transferMovesExactlyTheRequestedAmount() (gas: 74102)\n[PASS] test_transferOfMoreThanBalanceReverts() (gas: 34369)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 2.81ms (2.58ms CPU time)\n\nRan 3 tests for test/DeployLaunch.t.sol:DeployLaunchTest\n[PASS] test_deployProducesBoundTokenAndHookAtFlaggedAddress() (gas: 2048066)\n[PASS] test_deployRejectsManagerWithoutCode() (gas: 58679)\n[PASS] test_mineSaltIsDeterministicAndMatchesDeployment() (gas: 2381680)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 2.80ms (3.22ms CPU time)\n\nRan 26 tests for test/LaunchGuardHook.t.sol:LaunchGuardHookTest\n[PASS] testFuzz_afterTheWindowAnyAmountPasses(uint256,uint256) (runs: 256, μ: 189593, ~: 188818)\nLogs:\n  Bound result 77876325109436370496005072\n  Bound result 314110156\n\n[PASS] testFuzz_exactInputBuyIsAllowedIffRealizedOutputAtMostTheCap(uint256,bool) (runs: 256, μ: 216535, ~: 227417)\nLogs:\n  Bound result 49999999000000000000003759\n\n[PASS] testFuzz_exactOutputBuyIsAllowedIffAtMostTheCap(uint256,bool) (runs: 256, μ: 181679, ~: 182600)\nLogs:\n  Bound result 3758\n\n[PASS] test_callbacksRefuseCallersOtherThanThePoolManager() (gas: 96282)\n[PASS] test_capHoldsUntilTheLastSecondOfTheWindow() (gas: 418564)\n[PASS] test_capLiftsExactlyWhenTheWindowCloses() (gas: 534002)\n[PASS] test_constructorRejectsAddressWithoutTheFlags() (gas: 10239)\n[PASS] test_constructorRejectsZeroAddresses() (gas: 6500)\n[PASS] test_exactInputBuyOverTheCapIsRejectedInAfterSwap() (gas: 442083)\n[PASS] test_exactInputBuyUnderTheCapSucceeds() (gas: 355222)\n[PASS] test_exactOutputBuyAtTheCapSucceeds() (gas: 355576)\n[PASS] test_exactOutputBuyOverTheCapIsRejectedInBeforeSwap() (gas: 515730)\n[PASS] test_initializeEmitsAndRoutesThroughBeforeInitialize() (gas: 132197)\n[PASS] test_initializeRecordsTheLaunchForEachPool() (gas: 68996)\n[PASS] test_initializeRejectsLaunchTokenWithNoSupply() (gas: 10240015)\n[PASS] test_initializeRejectsPoolWithoutTheLaunchToken() (gas: 1560792)\n[PASS] test_longAfterTheWindowEverythingIsAllowed() (gas: 347210)\n[PASS] test_nobodyCanInitializeThePoolBeforeTheHookExists() (gas: 9648111)\n[PASS] test_permissionsMatchAddressAndDeclaredFlags() (gas: 29983)\n[PASS] test_rejectedSwapLeavesPoolAndBalancesUntouched() (gas: 440420)\n[PASS] test_repeatedBuysAtTheCapAreEachAllowed() (gas: 493222)\n[PASS] test_sellsAreNeverCappedDuringTheWindow() (gas: 445561)\n[PASS] test_smallExactOutputBuySucceeds() (gas: 354757)\n[PASS] test_unimplementedCallbacksRevert() (gas: 112535)\n[PASS] test_windowIsPerPool() (gas: 928927)\n[PASS] test_worksWithNativeQuoteCurrency() (gas: 630371)\nSuite result: ok. 26 passed; 0 failed; 0 skipped; finished in 115.88ms (128.68ms CPU time)\n\nRan 3 test suites in 116.59ms (121.50ms CPU time): 35 tests passed, 0 failed, 0 skipped (35 total tests)\n","passed":true},{"durationMs":59,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"LaunchGuardHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"LaunchToken.approve(address,uint256)\",\"LaunchToken.transfer(address,uint256)\",\"LaunchToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":11,\"README.md\":206,\"foundry.toml\":33,\"launch.json\":21,\"remappings.txt\":5,\"script/DeployLaunch.s.sol\":65,\"src/HookFlags.sol\":59,\"src/HookMiner.sol\":35,\"src/LaunchGuardHook.sol\":305,\"src/LaunchToken.sol\":19,\"test/DeployLaunch.t.sol\":50,\"test/LaunchGuardHook.t.sol\":572,\"test/LaunchToken.t.sol\":80,\"test/mocks/MockERC20.sol\":15},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"launch.json: constructor argument \"$POOL_MANAGER\" is neither an address nor an integer, and a launch manifest can express nothing else","evaluation":"checks","profile":"foundry","status":"rejected","submissionHash":"e63116736099bf4e37ce9cefca4381cb2c9316a7451b9cd016cf0485a0abbe67","verifiedTreeHash":"f12000e63f04010d8a1ef979cae77691f36850ac","verifierVersion":"0.1.0+9f8ef295"}]}