{"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":"a01dd7fe-7f49-4efa-a87d-bd32293d0bc2","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"11f9fc349bce4b7264b81be0aeb88558710cd8d3ef758ded4c09a9984d2b4b33","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":"eb8fbb8b64daf47a0b2ef61094af2086f2cb50424c349be7e623d1a19e9fa598","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":"769e319c6dbcd619953b093e0e371bd59bd0429061a3bb5399bdb6fb4cb390d9","dependsOn":["build_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","state":"accepted"},{"acceptedSubmissionHash":"c9a17a8a4258f8c3e88b071edd82da4d6915275a216312b85fc8c3c119ed4523","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":"2a121d67d4099d24f008cb1ee4fd3498ea547547cc7773711a0adb30ee30c492","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":"7e0b5394444e47b8eac6c646493b87e02e2692fb1f242532e8de3c01a8c4942b","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"d59561da3b378461b7c749f6bd5084477d44993aa94972ddea90f542535751df","skillId":"build-contract-project","tools":[]},"key":"build_contract_project","kind":"code","role":"implement","skillHash":"d59561da3b378461b7c749f6bd5084477d44993aa94972ddea90f542535751df","skillId":"build-contract-project","state":"accepted"},{"acceptedSubmissionHash":"3f55e5bc59798e487cc3a42cd06a945e92b3b21e23a82c061af2b5b948f7db18","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"3ba95bda9b3e80fb3d0854f82773d99bd3dd118c852e4310d3ff2f997b2f5c2f","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"4c76766b00591dfe982fe85d3de589fdb2f810e5137c301155d1c5e36a1d3b23","skillId":"write-foundry-tests","tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"4c76766b00591dfe982fe85d3de589fdb2f810e5137c301155d1c5e36a1d3b23","skillId":"write-foundry-tests","state":"accepted"}],"objective":"Launch test (TEST) with a Uniswap v4 hook on Robinhood Chain, paired with IMD.\nToken: test (TEST), the standard launch token: 1,000,000,000 supply, 18 decimals, plain transfers, no owner functions.\nbeforeInitialize accepts only the IMD launch factory, passed as the constructor argument $factory; any other initialization reverts; the hook serves only the pool the factory initializes. Constructor arguments: the Robinhood Chain PoolManager 0x8366a39CC670B4001A1121B8F6A443A643e40951, $owner and $factory.\nIn this hook, the token is test (TEST), the launch token; its total supply is that token's totalSupply().\nEvery pool-dependent view, state, window and limit is keyed by PoolId and isolated per pool; pass the pool id to each pool-dependent function.\nThe hook does exactly the following, and nothing else:\n1. Take 1.0% of the IMD side of every buy and sell as a hook fee, settled in IMD inside the swap that paid it (mint a claim when the PoolManager cannot pay it directly), and send it to 0x5Bff2a2FEcD1d6152676813b21346238733bEAB1. Expose claimFees(PoolId poolId) that anyone may call to redeem that pool's claims using the same distribution. Fees are computed on the amount actually traded; when the swap specifies its IMD amount and the pool fills only part of it, the swap reverts rather than overpaying.\n2. The owner may pause and unpause swaps separately by PoolId. Liquidity can always be removed while paused. Expose pause(PoolId poolId) and unpause(PoolId poolId) for the owner and paused(PoolId poolId) as a view.\n3. For the first 15 minutes of block timestamp after the pool's first swap, only wallets on the owner's buyer allowlist may buy; after that, anyone may buy. Track the window and allowlist separately by PoolId. Expose setAllowed(PoolId poolId, address[] accounts, bool allowed) for the owner and isAllowed(PoolId poolId, address wallet) as a view. A wallet means tx.origin; several wallets or a shared relayer can defeat the limit.\nOwner 0x5bff2a2fecd1d6152676813b21346238733beab1 may only: pause and unpause swaps, add and remove buyer allowlist addresses, through the functions named above. No other admin function, no backend signer, no upgrade path.\nFees are always taken from the IMD (pair currency) side, never from TEST; a fee the PoolManager cannot pay directly is minted as a claim the recipient can redeem. Fee recipient: 0x5Bff2a2FEcD1d6152676813b21346238733bEAB1.\nKeep every hook fee in IMD: do not convert the fee to ETH and do not use any other pool.\nThe launch token may be currency0 or currency1; handle both orders.\nThe hook uses no outside pool, token, price feed or service; every price it needs comes from its own pool.\nUse mulDiv for every percentage calculation, including fees, shares, limits and price moves, to avoid intermediate multiplication overflow.\nThe launch factory ($factory) may always add liquidity and collect fees with a zero-change modify. Enable beforeInitialize and only the callbacks the behavior above needs.\nPool: 10% of the total supply seeds the pool; 10% goes to the swarm; the remaining 80% goes to 0x5Bff2a2FEcD1d6152676813b21346238733bEAB1. economics.poolBps=1000, in basis points of the total supply.\nKeep the function names exactly as written so the storefront can show them. Composed with The Hooks Store block builder.","parentJobId":null,"planHash":"d5e8931827e1697181cda107e8634b7105dbfa378797e0100d03517850354a1a","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"a01dd7fe-7f49-4efa-a87d-bd32293d0bc2","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-2448-launch-test-test-uniswap"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51442","feedbackHash":"a74e6e4be2ac87806276da991cc3699ca3a3f2b0e5543859896d3d801fece847","nodeKey":"audit_economics","submissionHash":"11f9fc349bce4b7264b81be0aeb88558710cd8d3ef758ded4c09a9984d2b4b33","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52601","feedbackHash":"6e4c155265d5a875a5be2cfe834e809a8a7bc0dfb83300e027f48e6cfd749ce6","nodeKey":"audit_flow","submissionHash":"eb8fbb8b64daf47a0b2ef61094af2086f2cb50424c349be7e623d1a19e9fa598","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51197","feedbackHash":"393d8b418522206099849c52c19d7ebb1f0eb8c4d4e308f1cde68973f519a707","nodeKey":"audit_judge","submissionHash":"769e319c6dbcd619953b093e0e371bd59bd0429061a3bb5399bdb6fb4cb390d9","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52121","feedbackHash":"7b9e23731ebbeaf28f460bce50850b3638f7af96092abf265e9c6480d02d5171","nodeKey":"audit_math","submissionHash":"c9a17a8a4258f8c3e88b071edd82da4d6915275a216312b85fc8c3c119ed4523","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52151","feedbackHash":"ca0d3b505eb5a29da44e8d09d2014b0e90393adb36e8088a6fe0406584e06e7b","nodeKey":"audit_permissions","submissionHash":"2a121d67d4099d24f008cb1ee4fd3498ea547547cc7773711a0adb30ee30c492","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52647","feedbackHash":"79408428d35e4967c96d4197270de48fd2999242e89a29fdba1974bd3e1f86c3","nodeKey":"build_contract_project","submissionHash":"7e0b5394444e47b8eac6c646493b87e02e2692fb1f242532e8de3c01a8c4942b","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52274","feedbackHash":"ae26c0b686cb1c1d2dce8afe224d0d93df8fa43521eba99f46268e7424ef72f0","nodeKey":"manifest","submissionHash":"3f55e5bc59798e487cc3a42cd06a945e92b3b21e23a82c061af2b5b948f7db18","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51164","feedbackHash":"0339880ad2be8e9908c8876d0bdf8c29a31ce56e72e13301de3a178c62f2d8bb","nodeKey":"write_foundry_tests","submissionHash":"3ba95bda9b3e80fb3d0854f82773d99bd3dd118c852e4310d3ff2f997b2f5c2f","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"ad8e3489444b5bdbdcd7aa576dfe9bb3df3367c4697bf1455082cce973e33b4f","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"2b9b0095482c54e6","findings":[{"citation":"resolved","description":"Only beforeSwap checks the buyer allowlist (lines 194-197), and the hook fee is charged only on swaps. Anyone can add liquidity because the hook has no liquidity callbacks. That lets anyone hold a single-sided IMD position just past the current price, which works as a buy limit order. When any trader sells TEST during the window, the price moves through that range and turns the position's IMD into TEST. The LP then removes the position and keeps the TEST. So a wallet that is not on the allowlist acquires TEST while allowlistActive(poolId) is true, and it pays no 1% hook fee on its IMD-to-TEST conversion (only the seller pays the fee). The brief accepts that several wallets or a shared relayer can defeat the limit. This route needs no allowlisted wallet at all. Fixing it within the brief means either gating beforeAddLiquidity during the window for anyone but the factory (the factory may always add liquidity), or documenting the gap as an accepted limit of the tx.origin allowlist.","line":101,"path":"src/TestHook.sol","reproduction":"TEST is currency0, IMD is currency1, the pool is initialized at price 1 (tick 0), and the factory seeds [-120,120] with 1e24 liquidity. bob is not allowlisted. (1) A swap by bob (tx.origin) buying 1e18 IMD exact-in reverts with BuyerNotAllowed: expected. (2) bob calls modifyLiquidity(key, tickLower=-180, tickUpper=-120, liquidityDelta=1e22), funded only in IMD; no hook check runs. (3) alice sells 5e22 TEST exact-in. This starts the window, and allowlistActive(poolId) == true. (4) bob calls modifyLiquidity with -1e22 and receives about 30.6 TEST (30606921186237476658 wei) in a local run using the project's HookFixture with imdIs0=false. Expected: a wallet that is not allowlisted cannot acquire TEST from the pool during the window. Actual: bob acquires TEST and pays no hook fee.","severity":"low","snippet":"        p.beforeInitialize = true;\n        p.beforeSwap = true;\n        p.afterSwap = true;\n        p.beforeSwapReturnDelta = true;\n        p.afterSwapReturnDelta = true;","title":"Wallets not on the allowlist can buy TEST during the 15-minute window, with no hook fee, using IMD-only range orders"}],"hash":"11f9fc349bce4b7264b81be0aeb88558710cd8d3ef758ded4c09a9984d2b4b33","nodeId":"11fd485e-6ca0-4b95-be1c-2c88829a7dbb","outcome":"completed","summary":"I found one low-severity defect in the economics of `src/TestHook.sol`. Nothing reached medium or above. The findings and coverage are in `.imd-findings.json` (no source files changed), and the project's own suite passes (66 tests).\n\n**Finding (low): wallets not on the allowlist can still get TEST during the 15-minute window, and pay no hook fee doing it.** The allowlist is only checked when someone swaps. Adding liquidity has no checks at all, because the hook has no liquidity callbacks. So a wallet that isn't allowed to buy can deposit IMD alone into a narrow price range just below the current price. That works like a buy order: when anyone sells TEST during the window, the price moves through the range and turns that IMD into TEST. The wallet then withdraws and keeps the TEST, without paying the 1% hook fee. Only the seller pays the fee.\n- **Reproduction** (TEST as currency0, IMD as currency1, price 1, factory seeds liquidity across ticks -120 to 120):\n  1. bob, who isn't allowlisted, tries a direct buy. It reverts with `BuyerNotAllowed`, as it should.\n  2. bob adds 1e22 of IMD-only liquidity across ticks -180 to -120.\n  3. alice sells 5e22 TEST.\n  4. bob removes his position and receives about 30.6 TEST while `allowlistActive(poolId)` is still true.\n- **Verification:** I ran this as a throwaway Foundry test against the real PoolManager, then deleted it.\n- **Fix:** either have `beforeAddLiquidity` block anyone except the factory while the window is open, or document this as an accepted limit of the allowlist. The brief already accepts that several wallets or a shared relayer can get round it.\n\n**What holds:**\n- **Fee size:** the hook fee is 1% of the full IMD amount in all four swap types (buy and sell, exact input and exact output), in both token orders, computed with `mulDiv` and rounded down.\n- **No double charge or TEST fee:** the fee is never taken twice, and never in TEST.\n- **Partial fills:** a swap that names its IMD amount but is only partly filled reverts.\n- **Fee claims:** when the PoolManager can't pay the fee directly, the hook mints a claim. Every claim is backed, and `claimFees` pays only the fixed recipient, keeping any part it can't yet pay.\n- **Pause and window:** pausing blocks only swaps and doesn't restart or extend the window. The window starts on the first swap that actually trades.\n- **Static-analysis leads:** the tx.origin warnings describe the allowlist rule the brief asks for. The reentrancy warnings are covered by `nonReentrant` and the one-time claim flag.\n\n**Coverage:** every listed entry point has a row in the file. `beforeSwap` is marked as the finding. The others hold, and I added two rows for the fee and claim-backing invariants.\n\n**Not covered:** I didn't look at deployment or the manifest. The protected floor tests sit outside my area, and I didn't run them.","treeHash":null,"usage":{"cachedInputTokens":425057,"inputTokens":16,"model":"claude-opus-5-5","outputTokens":12006,"runtime":"claude","turns":8,"wallClockMs":138371}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"cdeffb0cd839cbf7","findings":[{"citation":"resolved","description":"Access-control review of beforeInitialize: besides the required sender == factory and single-binding checks, the hook hard-refuses every PoolKey whose fee is not 12500 or whose tickSpacing is not 60 (constants LP_FEE and TICK_SPACING at lines 28-29), and whose pair is not the hardcoded IMD address (line 23). 12500 is the hook-launch policy tier named in the reference and 60 matches the example manifest, so the launch as specified is accepted (verified by the project's own tests in both currency orders and by reading the protected Hook.protected.t.sol flow, which pranks the factory and uses IMD_POOL_FEE / IMD_TICK_SPACING). This is recorded so the manifest step and the judge can confirm pool.fee=12500, pool.tickSpacing=60 and pool.pairedCurrency=0x5f7bb59365ce557c26dbcaa4ee9d39a4b95b7127; if the policy row resolves any of them differently the factory's PoolManager.initialize reverts with InvalidPool and the launch is blocked. Not a defect against the brief as written; no code change is requested unless the manifest disagrees.","line":120,"path":"src/TestHook.sol","reproduction":"State: fresh TestHook deployed with (manager, owner, factory). Call, as the factory, manager.initialize(PoolKey{currency0/currency1 = sorted(TEST, IMD), fee: 12500, tickSpacing: 120, hooks: hook}, sqrtPrice). Expected if the launch policy used spacing 120: pool opens. Actual: the hook's beforeInitialize reverts InvalidPool and initialize fails (the project's test_onlyFactoryCanInitializeAndOnlyOnePool in test/TestHook.t.sol already demonstrates the tickSpacing=120 revert; same for fee 0 or 0x800000 in test_rejectsWrongPairFeeAndSpacing). With fee 12500 and spacing 60 the same call succeeds.","severity":"info","snippet":"                || key.fee != LP_FEE || key.tickSpacing != TICK_SPACING","title":"beforeInitialize pins the pool to LP fee 12500 and tick spacing 60; any other manifest pool terms cannot launch"}],"hash":"2a121d67d4099d24f008cb1ee4fd3498ea547547cc7773711a0adb30ee30c492","nodeId":"0bf38296-7a72-46e5-ac79-c0e99312ba05","outcome":"completed","summary":"The review is complete and `.imd-findings.json` holds the result. No source file was changed.\n\n**Outcome: no permission, trust-gap or asymmetry defect found in `TestHook` or `TestToken`.** The project's 66 tests pass on the pinned toolchain, and I traced each entry point against the Access Control, Trust Gap and Asymmetry guides plus the v4 security checklist.\n\nWhat I verified in depth:\n\n- **Access control.** Every callback is `onlyPoolManager`. `beforeInitialize` additionally requires the factory as sender and binds once. The owner's only powers are pause, unpause and setAllowed, checked on `msg.sender` against an immutable owner. There is no ownership transfer, upgrade, approval, arbitrary call or settable recipient. `unlockCallback` is reachable only through the hook's own `claimFees`, and its authorization flag is consumed before any external interaction.\n- **Fee accounting symmetry.** Using the vendored `Hooks.afterSwap` and `PoolManager.swap`, I confirmed the specified-side fee returned in `beforeSwap` and the unspecified-side fee returned in `afterSwap` both land on the swapper, that `take` or `mint` inside `afterSwap` is cancelled by the manager's credit after return, and that all four swap modes charge 1% of the gross IMD leg to the same fixed recipient. Partial fills of IMD-specified swaps revert as the brief requires.\n- **Trust gaps.** Pause blocks swaps only. Liquidity removal and the factory's zero-change collect have no callbacks and always pass. `claimFees` is permissionless but can only pay the fixed recipient and is capped to the manager's IMD balance. Claims cannot be moved by anyone since the hook grants no operator approval.\n- **Static-analysis leads.** The `tx.origin` lines implement the brief's explicit wallet definition. The reentrancy lines are covered by the shared guard, which I traced in both directions.\n\nOne informational note is recorded: `beforeInitialize` pins fee 12500, tick spacing 60 and the IMD address. These match the policy tier and the example manifest, so the launch proceeds, but the manifest step should confirm those exact values since any other pool terms revert with `InvalidPool`.\n\nCoverage has 15 rows: all 11 listed entry points answered, three invariants marked as holding, and one `unreached` row for live Robinhood Chain IMD token behaviour, which needs a fork rehearsal this review could not run.","treeHash":null,"usage":{"cachedInputTokens":795762,"inputTokens":226,"model":"claude-fable-5-1","outputTokens":34095,"runtime":"claude","turns":26,"wallClockMs":557859}},{"artifacts":[],"attempt":1,"bundleHash":"7add61df1cd7df86e3bad36425e9e3840d0c5233a618770e92a53bdab217756a","device":"a13784fa5cb209c3","findings":[],"hash":"3ba95bda9b3e80fb3d0854f82773d99bd3dd118c852e4310d3ff2f997b2f5c2f","nodeId":"4383461d-919a-4b87-8019-8de5758cbd7c","outcome":"completed","summary":"Added failure-path and fuzz tests for swap rollback, claim redemption after full LP withdrawal, and initialization safety in both token orders. Expanded hook invariants and added token balance/allowance invariants.\n\n`forge build` passed. Seeded `forge test`: **79 passed, 0 failed, 0 skipped**.\n\nOnly `test/` changed. No implementation defects found; live-chain verification remains separate.","treeHash":"633b2f3b5a4dc5408d8efe300745e777b5ebca82","usage":{"cachedInputTokens":1586304,"inputTokens":130185,"model":"gpt-6.1-sol","outputTokens":11427,"runtime":"codex","turns":5,"wallClockMs":321376}},{"artifacts":[],"attempt":1,"bundleHash":"9bfa58f52c10c595354284d49bd7d4ec23c5ce4625fc1fa9e316d841071da0bf","device":"7d2db8d9021f8063","findings":[],"hash":"3f55e5bc59798e487cc3a42cd06a945e92b3b21e23a82c061af2b5b948f7db18","nodeId":"ea9de2c0-61a5-4073-85db-62b5a2b7766d","outcome":"completed","summary":"Created [launch.json](/home/imd3/.identitymd/work/a01dd7fe-7f49-4efa-a87d-bd32293d0bc2/ea9de2c0-61a5-4073-85db-62b5a2b7766d/launch.json) with matching contract names, constructor placeholders, permissions, and pool settings.\n\n`forge build` passed. `forge test` passed all 66 tests with the specified fuzz seed. Only `launch.json` changed.","treeHash":"8985c2c69bb7e5dfcf84b2d87fa729d077966aa7","usage":{"cachedInputTokens":388324,"inputTokens":67703,"model":"gpt-6.1-sol","outputTokens":3636,"runtime":"codex","turns":3,"wallClockMs":68396}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"f2d7eadae2a3cfdf","findings":[{"citation":"resolved","description":"The allowlist is enforced only in beforeSwap (lines 193-197), and getHookPermissions (lines 100-106) enables no liquidity callback, so anyone may add liquidity at any time. A single-sided IMD position just past the current price works as a buy limit order. When any trader sells TEST through it during the window, the position's IMD is converted to TEST, and the LP withdraws the TEST while allowlistActive(poolId) is still true. That breaks the brief's guarantee that 'only wallets on the owner's buyer allowlist may buy' for the first 15 minutes, and it needs no allowlisted wallet or relayer (the brief accepts only those as ways around the limit). The conversion is not a swap, so the 1% IMD hook fee is not charged on it either. The README and the manifest notes do not disclose this route. Merges the economics specialist's finding (08182776…); I reproduced it independently. Severity is low: no funds are lost, and the bypass depends on sellers trading through the range, but it weakens a stated guarantee. Minimal fix within the brief: enable beforeAddLiquidity, and while the pool's window is active (or before the first swap) revert unless sender == factory or tx.origin is allowlisted for that PoolId. Leave removals unrestricted so the factory's zero-change fee collection and liquidity removal while paused still work. Re-mine the address for the new flag (0x28cc) and update launch.json permissions to match. Alternatively, document the route as an accepted limit.","line":193,"path":"src/TestHook.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 {IPoolManager} from \"v4-core/src/interfaces/IPoolManager.sol\";\nimport {IHooks} from \"v4-core/src/interfaces/IHooks.sol\";\nimport {Hooks} from \"v4-core/src/libraries/Hooks.sol\";\nimport {TickMath} from \"v4-core/src/libraries/TickMath.sol\";\nimport {IUnlockCallback} from \"v4-core/src/interfaces/callback/IUnlockCallback.sol\";\nimport {PoolKey} from \"v4-core/src/types/PoolKey.sol\";\nimport {PoolId} from \"v4-core/src/types/PoolId.sol\";\nimport {Currency} from \"v4-core/src/types/Currency.sol\";\nimport {BalanceDelta} from \"v4-core/src/types/BalanceDelta.sol\";\nimport {SwapParams, ModifyLiquidityParams} from \"v4-core/src/types/PoolOperation.sol\";\nimport {TestToken} from \"src/TestToken.sol\";\nimport {TestHook} from \"src/TestHook.sol\";\n\ncontract ScratchIMD is ERC20 {\n    constructor() ERC20(\"IMD\", \"IMD\") {}\n    function mint(address to, uint256 a) external { _mint(to, a); }\n}\n\ncontract Actor is IUnlockCallback {\n    IPoolManager immutable m;\n    constructor(IPoolManager m_) { m = m_; }\n    function init(PoolKey memory k, uint160 p) external { m.initialize(k, p); }\n    function modify(PoolKey memory k, int24 lo, int24 hi, int256 d) external returns (BalanceDelta) {\n        return abi.decode(m.unlock(abi.encode(uint8(0), k, abi.encode(ModifyLiquidityParams(lo, hi, d, 0)))), (BalanceDelta));\n    }\n    function swap(PoolKey memory k, SwapParams memory p) external returns (BalanceDelta) {\n        return abi.decode(m.unlock(abi.encode(uint8(1), k, abi.encode(p))), (BalanceDelta));\n    }\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        (uint8 op, PoolKey memory k, bytes memory payload) = abi.decode(data, (uint8, PoolKey, bytes));\n        BalanceDelta d;\n        if (op == 0) (d,) = m.modifyLiquidity(k, abi.decode(payload, (ModifyLiquidityParams)), \"\");\n        else d = m.swap(k, abi.decode(payload, (SwapParams)), \"\");\n        _s(k.currency0, d.amount0());\n        _s(k.currency1, d.amount1());\n        return abi.encode(d);\n    }\n    function _s(Currency c, int128 a) private {\n        if (a < 0) { m.sync(c); c.transfer(address(m), uint256(-int256(a))); m.settle(); }\n        else if (a > 0) m.take(c, address(this), uint256(int256(a)));\n    }\n}\n\ncontract RangeOrderBypassTest is Test {\n    address constant IMD = 0x5F7Bb59365ce557C26dbcAa4EE9d39A4b95B7127;\n    address constant OWNER = 0x5Bff2a2FEcD1d6152676813b21346238733bEAB1;\n\n    function test_nonAllowlistedWalletAcquiresTestDuringWindow() public {\n        vm.warp(1_000_000);\n        IPoolManager manager = IPoolManager(address(new PoolManager(address(this))));\n        Actor factory = new Actor(manager);\n        Actor seller = new Actor(manager);\n        Actor bobActor = new Actor(manager);\n        address bob = makeAddr(\"bob\");\n        deployCodeTo(\"RangeOrderBypass.t.sol:ScratchIMD\", IMD);\n        address tokenAddr = address(uint160(IMD) - 1); // TEST is currency0\n        deployCodeTo(\"TestToken.sol:TestToken\", tokenAddr);\n        TestToken test_ = TestToken(tokenAddr);\n        test_.transfer(address(factory), 1e26);\n        test_.transfer(address(seller), 1e26);\n        ScratchIMD(IMD).mint(address(factory), 1e27);\n        ScratchIMD(IMD).mint(address(bobActor), 1e27);\n\n        address at = address((uint160(makeAddr(\"hook\")) & ~uint160(Hooks.ALL_HOOK_MASK)) | uint160(0x20cc));\n        deployCodeTo(\"TestHook.sol:TestHook\", abi.encode(manager, OWNER, address(factory)), at);\n        TestHook hook = TestHook(at);\n        PoolKey memory key = PoolKey(Currency.wrap(tokenAddr), Currency.wrap(IMD), 12_500, 60, IHooks(at));\n        PoolId id = key.toId();\n        factory.init(key, 79228162514264337593543950336);\n        factory.modify(key, -120, 120, 1e24);\n\n        // A direct buy by bob is refused, as intended.\n        vm.prank(bob, bob);\n        vm.expectRevert();\n        bobActor.swap(key, SwapParams(false, -1e18, TickMath.MAX_SQRT_PRICE - 1));\n\n        // bob places an IMD-only range below the price: no hook check runs.\n        vm.prank(bob, bob);\n        BalanceDelta added = bobActor.modify(key, -180, -120, 1e22);\n        assertEq(added.amount0(), 0, \"range holds only IMD\");\n\n        // Someone sells TEST through bob's range; the window starts.\n        vm.prank(address(seller), address(seller));\n        seller.swap(key, SwapParams(true, -5e22, TickMath.MIN_SQRT_PRICE + 1));\n        assertTrue(hook.allowlistActive(id));\n        assertFalse(hook.isAllowed(id, bob));\n\n        uint256 before = test_.balanceOf(address(bobActor));\n        vm.prank(bob, bob);\n        bobActor.modify(key, -180, -120, -1e22);\n        uint256 got = test_.balanceOf(address(bobActor)) - before;\n        emit log_named_uint(\"TEST acquired by non-allowlisted bob inside the window\", got);\n        assertTrue(hook.allowlistActive(id));\n        // Intended: a non-allowlisted wallet cannot acquire TEST from the pool while the allowlist is active.\n        assertEq(got, 0, \"non-allowlisted wallet bought TEST through a range order during the allowlist window\");\n    }\n}","reproduction":"Setup (test/scratch/RangeOrderBypass.t.sol, below): fresh PoolManager. TEST is currency0 at IMD-1, IMD is currency1. Hook(manager, OWNER, factory). factory.initialize(key{fee 12500, spacing 60}, sqrtPrice 2^96), then factory adds [-120,120] with L=1e24. bob is not allowlisted. (1) vm.prank(bob,bob): exact-in buy of 1e18 IMD reverts BuyerNotAllowed, as expected. (2) vm.prank(bob,bob): modifyLiquidity(-180,-120,+1e22). It succeeds, no hook code runs, and the delta has amount0 == 0 (IMD only). (3) Another wallet sells 5e22 TEST exact-in. The window starts, and allowlistActive(poolId) == true. (4) vm.prank(bob,bob): modifyLiquidity(-180,-120,-1e22). Expected: a wallet that is not allowlisted cannot acquire TEST from the pool while allowlistActive is true. Actual: bob receives 30606921186237476658 wei TEST (about 30.6 TEST) with allowlistActive still true and isAllowed(poolId,bob) == false, and pays no hook fee. `forge test --match-path test/scratch/RangeOrderBypass.t.sol` fails on the current code with exactly that assertion.","severity":"low","snippet":"        bool buying = params.zeroForOne == imdIs0;\n        if (\n            buying && (!state.started || block.timestamp - state.firstSwapTimestamp < ALLOWLIST_DURATION)\n                && !state.allowed[tx.origin]\n        ) revert BuyerNotAllowed(poolId, tx.origin);","title":"The buyer allowlist only gates swaps: a wallet that is not allowlisted can buy TEST during the 15-minute window with an IMD-only range order, and pays no hook fee"}],"hash":"769e319c6dbcd619953b093e0e371bd59bd0429061a3bb5399bdb6fb4cb390d9","nodeId":"0c833ece-9eac-4056-af45-deaa26394242","outcome":"completed","summary":"I found one defect, rated low, and dropped the specialists' other three findings. The findings and coverage are in `.imd-findings.json`. The hook and token build cleanly, all 79 project tests pass, and both contracts are well under the size limits (hook runtime 7,918 bytes).\n\n**Finding 1 (low): the buyer allowlist doesn't cover range orders** (`src/TestHook.sol:193`)\n- **Problem:** the allowlist is only checked in `beforeSwap`, and the hook has no liquidity callbacks. Anyone can add an IMD-only position just past the current price, which acts as a buy order. When someone sells TEST through it during the 15-minute window, the position's IMD turns into TEST, and the owner withdraws it. No allowlisted wallet or relayer is involved, and no 1% hook fee is charged, because it isn't a swap. The README and the manifest notes don't mention this.\n- **Reproduction:** I wrote my own Foundry test. Bob is not allowlisted, and his direct buy reverts. He adds liquidity in the range [-180, -120] with only IMD. Someone else sells 5e22 TEST. Bob withdraws and ends up with about 30.6 TEST (30606921186237476658 wei) while `allowlistActive` is still true. This is the same amount the economics specialist reported, so I merged their finding into this one. The test source is attached as the finding's proof.\n- **Fix:** add a `beforeAddLiquidity` check that applies before the first swap and during the window, and only allows the factory or an allowlisted `tx.origin`. Removals stay unrestricted, so the factory's fee collection and removing liquidity while paused still work. This changes the hook's address mask to 0x28cc and the permissions in `launch.json`. The alternative is to document the route as an accepted limit.\n\n**Dropped specialist findings**\n- **Fee 12500 and tick spacing 60 are hard-coded (two info notes):** this is not a defect. It matches the policy tier and `launch.json`, so the launch can initialize.\n- **A 1-wei sell can start the allowlist clock (low):** this follows the brief, which starts the window at the pool's first swap, and a sell is a swap. The owner also can't fill the allowlist before initialization under any design, because the factory creates the hook and the pool in one transaction.\n\n**My own pass over the code**\n- **Fee math:** correct for all four swap shapes in both currency orders. The fee is 1% of the IMD side via mulDiv. A partial fill where the swap names its IMD amount reverts. Otherwise the fee is on the amount actually traded.\n- **Fee settlement:** the fee is paid directly when the PoolManager holds enough IMD, and minted as a claim otherwise. `claimFees` redeems claims to the fixed recipient, and the hook's balances net to zero on every path.\n- **Owner and constructor:** the owner can only pause, unpause and edit the allowlist, all keyed by pool id. Constructor arguments and permissions in `launch.json` match the code.\n\nAll 11 entry points have a coverage row, plus two invariant rows. The tree is as I found it: the scratch test and Forge's `out/` and `cache/` are deleted.","treeHash":null,"usage":{"cachedInputTokens":932792,"inputTokens":24,"model":"claude-opus-5-5","outputTokens":20246,"runtime":"claude","turns":13,"wallClockMs":241422}},{"artifacts":[],"attempt":1,"bundleHash":"3892e67c905862bccf3cd2b573ae324c45a039c39434fbda20194e44498b2c0f","device":"db7554a772b3fbf3","findings":[],"hash":"7e0b5394444e47b8eac6c646493b87e02e2692fb1f242532e8de3c01a8c4942b","nodeId":"ccaf8e7b-2d23-4dd9-877a-f3c48388b25e","outcome":"completed","summary":"Implemented TEST and the immutable hook with IMD fees and claims, pool-specific pause controls, and the buyer allowlist window. Dependencies and deployment documentation are included.\n\nValidation passed: `forge build`, all 66 tests, and `forge fmt --check`.\n\nFull fork rehearsal could not run: Foundry’s RPC connection was refused. No transactions were broadcast.","treeHash":"a1c13021b2072460b15164c764f9dfd50694c33a","usage":{"cachedInputTokens":1514496,"inputTokens":253219,"model":"gpt-6-astra","outputTokens":39454,"runtime":"codex","turns":8,"wallClockMs":920435}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"bdd9b74dce66953d","findings":[],"hash":"c9a17a8a4258f8c3e88b071edd82da4d6915275a216312b85fc8c3c119ed4523","nodeId":"4aaac27c-e159-48b2-a6e2-ccde2357ce23","outcome":"completed","summary":"The review is complete. The findings file is written with an empty findings list and a 15-row coverage record, and the scratch tests are removed so the tree is unchanged.\n\n**Result: no substantiated defect in the Math Precision, Boundary or Numerical Gap area.**\n\nWhat I checked and how:\n\n- **Fee formulas.** For IMD-specified swaps the hook charges `floor(|A|/100)` on exact input and `floor(A/99)` on exact output. For TEST-specified swaps it charges `floor(B/100)` on sells and `floor(B/99)` on buys from the core delta. I proved algebraically that `floor(B/99)` equals `floor((B + floor(B/99))/100)`, so every mode is exactly 1% of the gross IMD leg rounded down, as the README states. All four modes use mulDiv, and int256.min is handled without signed overflow.\n- **Delta routing against v4-core.** I traced the Pool swap loop and the step math to confirm a full fill always leaves the core specified delta equal to the hook-adjusted amount, so the equality check in `afterSwap` is exact and only partial fills revert. Hook deltas net to zero through take, mint or burn on every path.\n- **Boundaries probed with scratch tests in both currency orders.** Launch-realistic price from a 2,500 IMD cap with 100M TEST seeded one-sided, an IMD-specified sell against a pool holding no IMD, a TEST sell with no IMD liquidity, 1 wei and 99 wei legs, and the claim cap when the manager's IMD belongs to LPs. All behaved as specified.\n- **Static-analysis leads.** The tx.origin, timestamp and reentrancy lines are either required by the brief or benign, and the shared guard blocks claim reentry during fee payout. None reproduced as a defect.\n\nTwo observations that are by design rather than defects, recorded in the coverage rows: IMD legs under 100 wei pay no fee, and a TEST sell into a pool with no IMD moves the price to the limit at zero cost, which is inherent v4 behaviour that moves no value and does not start the allowlist window.","treeHash":null,"usage":{"cachedInputTokens":861102,"inputTokens":258,"model":"claude-fable-5-1","outputTokens":37554,"runtime":"claude","turns":26,"wallClockMs":558374}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"cc86b49d3c401897","findings":[{"citation":"resolved","description":"The allowlist window is keyed off the pool's first swap of either direction (afterSwap, lines 229-233), and sells are never gated (beforeSwap line 194 only guards `buying`). After the factory's launch transaction the owner (80% recipient) and the swarm MerkleDistributor claimants hold TEST, so any such holder can sell 1 wei of TEST and start the clock. 900 seconds later `allowlistActive` is false and anyone may buy, even if the owner has not yet called `setAllowed` once. The owner cannot populate the allowlist ahead of the launch transaction because `setAllowed`, `pause` and `unpause` all go through `_pool`, which reverts `UnknownPool` until beforeInitialize has bound the pool (line 285), so there is always a post-launch gap in which an unprivileged party can consume the window. The brief literally defines the window as starting at the first swap, and the README documents that a sell can start it, so this is a weakened guarantee rather than a spec violation: the intended head start for allowlisted buyers can be reduced to zero by a non-allowlisted third party at a cost of 1 wei of TEST plus gas. No funds are lost. Proposed minimal fix within the agreed design: start the clock only on the first buy (`buying` is already computed in beforeSwap; store it or recompute in afterSwap and require it before setting `started`), or let the owner call `setAllowed` before binding by keying allowlist storage on the PoolId without the `_pool` existence check.","line":229,"path":"src/TestHook.sol","reproduction":"State: pool initialized by factory and seeded; owner has made no calls. 1) An address holding 1 wei TEST (a swarm claimant) swaps TEST->IMD exact-input with amountSpecified = -1 via any router (tx.origin not allowlisted). Expected (intent): the allowlist head start cannot be consumed by non-allowlisted parties. Actual: afterSwap sets started=true and firstSwapTimestamp=block.timestamp. 2) vm.warp(+900). 3) An arbitrary wallet buys IMD->TEST exact-input 1e18; beforeSwap passes because `block.timestamp - firstSwapTimestamp < 900` is false. Also: before step 1, `vm.prank(owner); hook.setAllowed(poolId, [wallet], true)` on a hook whose pool is not yet initialized reverts with UnknownPool(poolId). Scratch test test/scratch/WindowStart.t.sol (tests test_dustSellStartsAllowlistClockWithoutOwner and test_ownerCannotPreListBeforeInitialize) passes against the current code, demonstrating both behaviours.","severity":"low","snippet":"        if (!state.started && (delta.amount0() != 0 || delta.amount1() != 0)) {\n            state.started = true;\n            state.firstSwapTimestamp = block.timestamp;","title":"Any TEST holder can start the 15-minute allowlist clock with a 1-wei sell before the owner has listed a single wallet; the owner cannot pre-list before initialization"},{"citation":"resolved","description":"beforeInitialize reverts InvalidPool unless fee == 12500, tickSpacing == 60 and one currency is exactly 0x5F7Bb59365ce557C26dbcAa4EE9d39A4b95B7127. The fee tier matches the launch policy and the IMD address matches the brief, and the example manifest uses tickSpacing 60, so this is consistent today. It is recorded so the manifest step writes pool.tickSpacing = 60 and pool.pairedCurrency = 0x5f7bb59365ce557c26dbcaa4ee9d39a4b95b7127; any other value makes the factory's PoolManager.initialize revert inside the hook and the launch fails in the deployer's simulation. Not a code defect given the stated pool; no fix needed unless the manifest assignment names a different spacing.","line":120,"path":"src/TestHook.sol","reproduction":"vm.prank(factory); manager.initialize(PoolKey{currency0: IMD, currency1: TEST, fee: 12500, tickSpacing: 10, hooks: hook}, sqrtPrice) -> reverts with WrappedError(hook, beforeInitialize.selector, InvalidPool()). Same key with tickSpacing 60 initializes. The existing test test_rejectsWrongPairFeeAndSpacing in test/TestHook.t.sol exercises the tickSpacing=120 case.","severity":"info","snippet":"                || key.fee != LP_FEE || key.tickSpacing != TICK_SPACING","title":"beforeInitialize hard-codes tick spacing 60 and the IMD pair address; a manifest that names any other tickSpacing (or pairedCurrency) cannot launch"}],"hash":"eb8fbb8b64daf47a0b2ef61094af2086f2cb50424c349be7e623d1a19e9fa598","nodeId":"d7f14508-d85d-4716-b93a-0068dcce5ec5","outcome":"completed","summary":"Review complete. The findings file is valid, both snippets match the tree at their cited lines, and no tracked file was modified (only `.imd-findings.json` and the scratch test under `test/scratch/`).\n\n**Outcome**\n\nThe hook's control flow in my area (Execution Trace, Periphery, First Principles) holds up. I traced every swap path against the vendored v4-core `Hooks.beforeSwap`/`afterSwap` and `PoolManager.swap/take/mint/burn`: the specified-IMD fee is returned once in beforeSwap and verified by a strict full-fill check in afterSwap, the unspecified-IMD fee is grossed up correctly from the core delta, the hook's delta nets to zero on both the direct-take and claim-mint paths, and claims are fully backed by the swapper's own settlement. The claim redemption path is correctly gated by a consumed authorization flag and cannot run inside another unlock. The vendored `SafeCast.toInt128` and `FullMath.mulDiv` are checked. The slither/aderyn leads (tx.origin, reentrancy ordering) are either required by the brief or already guarded, and I did not promote them.\n\n**Findings reported**\n\n- **Low:** any TEST holder can start the 15-minute allowlist clock by selling 1 wei, and the owner cannot pre-populate the allowlist until the pool is bound, so a non-allowlisted party can consume the head start before the owner acts. Proven with a scratch test at `test/scratch/WindowStart.t.sol` (both behaviours pass against current code). No fund loss, and the brief's literal wording permits it, so I rated it low with a minimal fix suggestion.\n- **Info:** `beforeInitialize` hard-codes tick spacing 60 and the IMD address, so the manifest must name exactly those or the launch reverts.\n\n**Coverage**\n\nAll 11 listed entry points have rows (9 hold, 2 reference finding 1), plus five invariant/lead rows. Nothing was left unreached. The one area I could not exercise is a real-IMD fork rehearsal, which the README also notes was not run; everything else was checked against the vendored PoolManager.","treeHash":null,"usage":{"cachedInputTokens":1113082,"inputTokens":322,"model":"claude-fable-5-1","outputTokens":43971,"runtime":"claude","turns":24,"wallClockMs":613814}}],"verification":[{"checks":[{"durationMs":4899,"exitCode":0,"name":"build","output":"Compiling 81 files with Solc 0.8.26\nSolc 0.8.26 finished in 4.81s\nCompiler run successful!\n","passed":true},{"durationMs":2049,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 3 tests for test/Deployment.t.sol:DeploymentTest\n[PASS] test_constructorAcceptsNonzeroArgumentsWithoutCodeOnEmptyChain() (gas: 50890)\n[PASS] test_constructorRejectsZeroArgumentsAndWrongFlags() (gas: 6949)\n[PASS] test_realCreate2DeploymentFromFactoryHasCorrectOwnerAndFlags() (gas: 51335292)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 193.11ms (80.76ms CPU time)\n\nRan 28 tests for test/TestHook.t.sol:TestHookIMD0Test\n[PASS] testFuzz_feeConservation(bool,bool,uint256) (runs: 256, μ: 831839, ~: 829276)\nLogs:\n  Bound result 57500005643297870872\n\n[PASS] test_allFourSwapModesPayExactlyOnePercentOfGrossIMD() (gas: 2250108)\n[PASS] test_allowlistStartsOnFirstSwapAndExpiresAtExactly900Seconds() (gas: 920023)\n[PASS] test_callbacksRejectNonManagerAndForgedUnlock() (gas: 160406)\n[PASS] test_dustAndFeeRoundingBoundariesInAllModes() (gas: 13289666)\n[PASS] test_failedDirectTransferFallsBackForAllSwapModes() (gas: 2945481)\n[PASS] test_freshTokenOnlyPoolMintsClaimsOnExactInputBuy() (gas: 1179502)\n[PASS] test_freshTokenOnlyPoolMintsClaimsOnExactOutputBuy() (gas: 1176937)\n[PASS] test_initializationPermissionsAndSupply() (gas: 125098)\n[PASS] test_multipleSwapsWithinOneUnlockAccumulateClaimsExactly() (gas: 932782)\n[PASS] test_noLiquidityCannotConsumeFeeOrStartWindow() (gas: 1030291)\n[PASS] test_noOwnershipTransferOrOtherAdminSelectors() (gas: 153207)\n[PASS] test_onlyFactoryCanInitializeAndOnlyOnePool() (gas: 273127)\n[PASS] test_onlyOwnerMayChangePauseAndAllowlist() (gas: 191636)\n[PASS] test_partialClaimRedemptionPreservesRemainder() (gas: 1302241)\n[PASS] test_partialSpecifiedIMDBuyRevertsWithoutFeeOrWindow() (gas: 703780)\n[PASS] test_partialSpecifiedIMDSellRevertsWithoutFeeOrWindow() (gas: 694481)\n[PASS] test_partialUnspecifiedIMDUsesOnlyActualTrade() (gas: 1023791)\n[PASS] test_pauseBlocksBothDirectionsButFactoryAndLPsCanExit() (gas: 2147895)\n[PASS] test_pauseDoesNotRestartOrExtendWindow() (gas: 749251)\n[PASS] test_reentrantFeeAndRedemptionCallsCannotSpendClaimsTwice() (gas: 1286821)\n[PASS] test_rejectsWrongPairFeeAndSpacing() (gas: 341814)\n[PASS] test_sellStartsWindowAndDoesNotNeedAllowlist() (gas: 1013474)\n[PASS] test_separateLaunchesHaveIsolatedWindowsPauseAllowlistAndClaims() (gas: 1557815)\n[PASS] test_sharedRelayerUsesOriginAndIgnoresHookData() (gas: 894382)\n[PASS] test_unknownPoolCannotReadOrMutateAnotherPool() (gas: 265931)\n[PASS] test_unrepresentableFeeRevertsWithSafeCastInsteadOfArithmeticOverflow() (gas: 121555)\n[PASS] test_zeroTimestampIsNotAnUnstartedSentinel() (gas: 712138)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 230.74ms (265.35ms CPU time)\n\nRan 5 tests for test/TestToken.t.sol:TestTokenTest\n[PASS] testFuzz_plainTransfersAndAllowances(uint256) (runs: 256, μ: 235842, ~: 236019)\nLogs:\n  Bound result 2228\n\n[PASS] test_factoryAllocationAndContractTransfers() (gas: 1519178)\n[PASS] test_insufficientBalanceAndAllowanceRevert() (gas: 67540)\n[PASS] test_metadataAndFactoryMint() (gas: 1287070)\n[PASS] test_noAdminMintBurnOrUpgradeSelectors() (gas: 286938)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 235.37ms (236.11ms CPU time)\n\nRan 6 tests for test/HookAtomicity.t.sol:HookAtomicityIMD1Test\n[PASS] testFuzz_claimSurvivesFullLiquidityExitWhilePaused(uint96,bool,bool) (runs: 1000, μ: 1113830, ~: 1114805)\nLogs:\n  Bound result 9000000000001334719\n\n[PASS] testFuzz_failedInputSettlementRollsBackFeePriceAndWindow(uint96,bool) (runs: 1000, μ: 1450624, ~: 1416823)\nLogs:\n  Bound result 19045\n\n[PASS] test_emptyAndDuplicateAllowlistUpdatesAreIdempotent() (gas: 314045)\n[PASS] test_failedCoreInitializationDoesNotBindHook() (gas: 270177)\n[PASS] test_nestedUnlockFailureCannotStrandClaimAuthorization() (gas: 1176872)\n[PASS] test_unknownKeyCallbacksCannotAffectBoundPool() (gas: 250304)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 237.06ms (459.09ms CPU time)\n\nRan 6 tests for test/HookAtomicity.t.sol:HookAtomicityIMD0Test\n[PASS] testFuzz_claimSurvivesFullLiquidityExitWhilePaused(uint96,bool,bool) (runs: 1000, μ: 1113795, ~: 1110376)\nLogs:\n  Bound result 9000076817546445372\n\n[PASS] testFuzz_failedInputSettlementRollsBackFeePriceAndWindow(uint96,bool) (runs: 1000, μ: 1446115, ~: 1412311)\nLogs:\n  Bound result 103\n\n[PASS] test_emptyAndDuplicateAllowlistUpdatesAreIdempotent() (gas: 314045)\n[PASS] test_failedCoreInitializationDoesNotBindHook() (gas: 270093)\n[PASS] test_nestedUnlockFailureCannotStrandClaimAuthorization() (gas: 1189230)\n[PASS] test_unknownKeyCallbacksCannotAffectBoundPool() (gas: 250684)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 237.11ms (471.61ms CPU time)\n\nRan 28 tests for test/TestHook.t.sol:TestHookIMD1Test\n[PASS] testFuzz_feeConservation(bool,bool,uint256) (runs: 256, μ: 832220, ~: 833437)\nLogs:\n  Bound result 57500005643297870872\n\n[PASS] test_allFourSwapModesPayExactlyOnePercentOfGrossIMD() (gas: 2249846)\n[PASS] test_allowlistStartsOnFirstSwapAndExpiresAtExactly900Seconds() (gas: 912933)\n[PASS] test_callbacksRejectNonManagerAndForgedUnlock() (gas: 160768)\n[PASS] test_dustAndFeeRoundingBoundariesInAllModes() (gas: 13267588)\n[PASS] test_failedDirectTransferFallsBackForAllSwapModes() (gas: 2945219)\n[PASS] test_freshTokenOnlyPoolMintsClaimsOnExactInputBuy() (gas: 1167244)\n[PASS] test_freshTokenOnlyPoolMintsClaimsOnExactOutputBuy() (gas: 1166568)\n[PASS] test_initializationPermissionsAndSupply() (gas: 125098)\n[PASS] test_multipleSwapsWithinOneUnlockAccumulateClaimsExactly() (gas: 919542)\n[PASS] test_noLiquidityCannotConsumeFeeOrStartWindow() (gas: 1017501)\n[PASS] test_noOwnershipTransferOrOtherAdminSelectors() (gas: 153207)\n[PASS] test_onlyFactoryCanInitializeAndOnlyOnePool() (gas: 273169)\n[PASS] test_onlyOwnerMayChangePauseAndAllowlist() (gas: 191636)\n[PASS] test_partialClaimRedemptionPreservesRemainder() (gas: 1289883)\n[PASS] test_partialSpecifiedIMDBuyRevertsWithoutFeeOrWindow() (gas: 697636)\n[PASS] test_partialSpecifiedIMDSellRevertsWithoutFeeOrWindow() (gas: 700487)\n[PASS] test_partialUnspecifiedIMDUsesOnlyActualTrade() (gas: 1015627)\n[PASS] test_pauseBlocksBothDirectionsButFactoryAndLPsCanExit() (gas: 2147925)\n[PASS] test_pauseDoesNotRestartOrExtendWindow() (gas: 755839)\n[PASS] test_reentrantFeeAndRedemptionCallsCannotSpendClaimsTwice() (gas: 1286873)\n[PASS] test_rejectsWrongPairFeeAndSpacing() (gas: 341910)\n[PASS] test_sellStartsWindowAndDoesNotNeedAllowlist() (gas: 1020663)\n[PASS] test_separateLaunchesHaveIsolatedWindowsPauseAllowlistAndClaims() (gas: 1551297)\n[PASS] test_sharedRelayerUsesOriginAndIgnoresHookData() (gas: 887110)\n[PASS] test_unknownPoolCannotReadOrMutateAnotherPool() (gas: 265931)\n[PASS] test_unrepresentableFeeRevertsWithSafeCastInsteadOfArithmeticOverflow() (gas: 121537)\n[PASS] test_zeroTimestampIsNotAnUnstartedSentinel() (gas: 718726)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 237.53ms (261.94ms CPU time)\n\nRan 1 test for test/TokenStateful.t.sol:TokenStatefulTest\n[PASS]\nTokenStatefulTest invariants:\n[PASS] invariant_onlyAuthorizedAmountsCanBeSpent\n[PASS] invariant_supplyAndEveryHolderMatchTheTransferLedger\n TokenStatefulTest invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭----------------------+------------------+-------+---------+----------╮\n| Contract             | Selector         | Calls | Reverts | Discards |\n+======================================================================+\n| TokenSequenceHandler | approve          | 1383  | 0       | 0        |\n|----------------------+------------------+-------+---------+----------|\n| TokenSequenceHandler | approvedOverdraw | 1411  | 0       | 0        |\n|----------------------+------------------+-------+---------+----------|\n| TokenSequenceHandler | invalidRecipient | 1305  | 0       | 0        |\n|----------------------+------------------+-------+---------+----------|\n| TokenSequenceHandler | overdraw         | 1358  | 0       | 0        |\n|----------------------+------------------+-------+---------+----------|\n| TokenSequenceHandler | transfer         | 1369  | 0       | 0        |\n|----------------------+------------------+-------+---------+----------|\n| TokenSequenceHandler | transferFrom     | 1366  | 0       | 0        |\n╰----------------------+------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 900\n  Bound result 5818\n  Bound result 7858\n  Bound result 0\n  Bound result 0\n  Bound result 592252179955193009630158047\n  Bound result 494\n  Bound result 2094\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 6917\n  Bound result 0\n  Bound result 417\n  Bound result 0\n  Bound result 8317\n  Bound result 6842\n  Bound result 0\n  Bound result 1677\n  Bound result 865106243299073272804491870\n  Bound result 3059\n  Bound result 30\n  Bound result 5294\n  Bound result 7\n  Bound result 796\n  Bound result 8\n  Bound result 120\n  Bound result 101\n  Bound result 0\n  Bound result 0\n  Bound result 522869804293115306181130456\n  Bound result 912\n  Bound result 2771\n  Bound result 2541\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.78s (1.78s CPU time)\n\nRan 1 test for test/HookInvariant.t.sol:HookIMD0InvariantTest\n[PASS]\nHookIMD0InvariantTest invariants:\n[PASS] invariant_allFeesArePaidOrBackedByPoolClaims\n[PASS] invariant_controlsMatchIndependentOwnerActions\n[PASS] invariant_currencyConservationAndTokenSupply\n[PASS] invariant_firstSwapWindowCannotBeReset\n HookIMD0InvariantTest invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭-------------+--------------------+-------+---------+----------╮\n| Contract    | Selector           | Calls | Reverts | Discards |\n+===============================================================+\n| HookHandler | advance            | 906   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | collectLPFees      | 865   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | liquidityRoundTrip | 936   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | redeem             | 926   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | setAllowed         | 876   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | setPause           | 906   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | setTransferFailure | 910   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | trade              | 904   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | unauthorizedAdmin  | 963   | 0       | 0        |\n╰-------------+--------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 444\n  Bound result 0\n  Bound result 9999999000000001804\n  Bound result 6443\n  Bound result 416\n  Bound result 384\n  Bound result 186\n  Bound result 374\n  Bound result 3139\n  Bound result 9999999000000011129\n  Bound result 9999999000000012910\n  Bound result 91\n  Bound result 4175\n  Bound result 492\n  Bound result 10079\n  Bound result 600\n  Bound result 9999999000000006444\n  Bound result 3988703684301201161644\n  Bound result 9999999000000000677\n  Bound result 9999999000000004410\n  Bound result 9999999000000003427\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.80s (1.80s CPU time)\n\nRan 1 test for test/HookInvariant.t.sol:HookIMD1InvariantTest\n[PASS]\nHookIMD1InvariantTest invariants:\n[PASS] invariant_allFeesArePaidOrBackedByPoolClaims\n[PASS] invariant_controlsMatchIndependentOwnerActions\n[PASS] invariant_currencyConservationAndTokenSupply\n[PASS] invariant_firstSwapWindowCannotBeReset\n HookIMD1InvariantTest invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭-------------+--------------------+-------+---------+----------╮\n| Contract    | Selector           | Calls | Reverts | Discards |\n+===============================================================+\n| HookHandler | advance            | 906   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | collectLPFees      | 865   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | liquidityRoundTrip | 936   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | redeem             | 926   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | setAllowed         | 876   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | setPause           | 906   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | setTransferFailure | 910   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | trade              | 904   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | unauthorizedAdmin  | 963   | 0       | 0        |\n╰-------------+--------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 426\n  Bound result 0\n  Bound result 9999999000000001804\n  Bound result 6501\n  Bound result 416\n  Bound result 384\n  Bound result 24\n  Bound result 475\n  Bound result 3139\n  Bound result 9999999000000011129\n  Bound result 9999999000000013007\n  Bound result 91\n  Bound result 4148\n  Bound result 491\n  Bound result 8334\n  Bound result 600\n  Bound result 9999999000000006501\n  Bound result 3988703684301201161644\n  Bound result 9999999000000000549\n  Bound result 9999999000000004948\n  Bound result 9999999000000003475\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.95s (1.94s CPU time)\n\nRan 9 test suites in 1.95s (6.90s CPU time): 79 tests passed, 0 failed, 0 skipped (79 total tests)\n","passed":true},{"durationMs":60,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"TestHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"TestHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"TestHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"TestHook.claimFees(bytes32)\",\"TestHook.pause(bytes32)\",\"TestHook.setAllowed(bytes32,address[],bool)\",\"TestHook.unlockCallback(bytes)\",\"TestHook.unpause(bytes32)\",\"TestToken.approve(address,uint256)\",\"TestToken.transfer(address,uint256)\",\"TestToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":3,\"README.md\":124,\"docs/SECURITY.md\":51,\"docs/dependencies.json\":22,\"foundry.toml\":22,\"remappings.txt\":4,\"src/TestHook.sol\":301,\"src/TestToken.sol\":11,\"test/Deployment.t.sol\":79,\"test/HookAtomicity.t.sol\":187,\"test/HookInvariant.t.sol\":230,\"test/TESTING.md\":25,\"test/TestHook.t.sol\":675,\"test/TestToken.t.sol\":97,\"test/TokenStateful.t.sol\":134,\"test/helpers/MockIMD.sol\":38,\"test/helpers/PoolActor.sol\":78},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"3ba95bda9b3e80fb3d0854f82773d99bd3dd118c852e4310d3ff2f997b2f5c2f","verifiedTreeHash":"633b2f3b5a4dc5408d8efe300745e777b5ebca82","verifierVersion":"0.1.0+560ab6a2"},{"checks":[{"durationMs":3364,"exitCode":0,"name":"build","output":"Compiling 79 files with Solc 0.8.26\nSolc 0.8.26 finished in 3.29s\nCompiler run successful!\n","passed":true},{"durationMs":441,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 5 tests for test/TestToken.t.sol:TestTokenTest\n[PASS] testFuzz_plainTransfersAndAllowances(uint256) (runs: 256, μ: 235609, ~: 236037)\nLogs:\n  Bound result 103140346790993868343760\n\n[PASS] test_factoryAllocationAndContractTransfers() (gas: 1519178)\n[PASS] test_insufficientBalanceAndAllowanceRevert() (gas: 67540)\n[PASS] test_metadataAndFactoryMint() (gas: 1287070)\n[PASS] test_noAdminMintBurnOrUpgradeSelectors() (gas: 286938)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 9.49ms (9.72ms CPU time)\n\nRan 3 tests for test/Deployment.t.sol:DeploymentTest\n[PASS] test_constructorAcceptsNonzeroArgumentsWithoutCodeOnEmptyChain() (gas: 50890)\n[PASS] test_constructorRejectsZeroArgumentsAndWrongFlags() (gas: 6949)\n[PASS] test_realCreate2DeploymentFromFactoryHasCorrectOwnerAndFlags() (gas: 51335292)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 36.72ms (36.41ms CPU time)\n\nRan 28 tests for test/TestHook.t.sol:TestHookIMD0Test\n[PASS] testFuzz_feeConservation(bool,bool,uint256) (runs: 256, μ: 831703, ~: 829282)\nLogs:\n  Bound result 14264337671979831211\n\n[PASS] test_allFourSwapModesPayExactlyOnePercentOfGrossIMD() (gas: 2250108)\n[PASS] test_allowlistStartsOnFirstSwapAndExpiresAtExactly900Seconds() (gas: 920023)\n[PASS] test_callbacksRejectNonManagerAndForgedUnlock() (gas: 160406)\n[PASS] test_dustAndFeeRoundingBoundariesInAllModes() (gas: 13289666)\n[PASS] test_failedDirectTransferFallsBackForAllSwapModes() (gas: 2945481)\n[PASS] test_freshTokenOnlyPoolMintsClaimsOnExactInputBuy() (gas: 1179502)\n[PASS] test_freshTokenOnlyPoolMintsClaimsOnExactOutputBuy() (gas: 1176937)\n[PASS] test_initializationPermissionsAndSupply() (gas: 125098)\n[PASS] test_multipleSwapsWithinOneUnlockAccumulateClaimsExactly() (gas: 932782)\n[PASS] test_noLiquidityCannotConsumeFeeOrStartWindow() (gas: 1030291)\n[PASS] test_noOwnershipTransferOrOtherAdminSelectors() (gas: 153207)\n[PASS] test_onlyFactoryCanInitializeAndOnlyOnePool() (gas: 273127)\n[PASS] test_onlyOwnerMayChangePauseAndAllowlist() (gas: 191636)\n[PASS] test_partialClaimRedemptionPreservesRemainder() (gas: 1302241)\n[PASS] test_partialSpecifiedIMDBuyRevertsWithoutFeeOrWindow() (gas: 703780)\n[PASS] test_partialSpecifiedIMDSellRevertsWithoutFeeOrWindow() (gas: 694481)\n[PASS] test_partialUnspecifiedIMDUsesOnlyActualTrade() (gas: 1023791)\n[PASS] test_pauseBlocksBothDirectionsButFactoryAndLPsCanExit() (gas: 2147895)\n[PASS] test_pauseDoesNotRestartOrExtendWindow() (gas: 749251)\n[PASS] test_reentrantFeeAndRedemptionCallsCannotSpendClaimsTwice() (gas: 1286821)\n[PASS] test_rejectsWrongPairFeeAndSpacing() (gas: 341814)\n[PASS] test_sellStartsWindowAndDoesNotNeedAllowlist() (gas: 1013474)\n[PASS] test_separateLaunchesHaveIsolatedWindowsPauseAllowlistAndClaims() (gas: 1557815)\n[PASS] test_sharedRelayerUsesOriginAndIgnoresHookData() (gas: 894382)\n[PASS] test_unknownPoolCannotReadOrMutateAnotherPool() (gas: 265931)\n[PASS] test_unrepresentableFeeRevertsWithSafeCastInsteadOfArithmeticOverflow() (gas: 121555)\n[PASS] test_zeroTimestampIsNotAnUnstartedSentinel() (gas: 712138)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 36.90ms (84.16ms CPU time)\n\nRan 28 tests for test/TestHook.t.sol:TestHookIMD1Test\n[PASS] testFuzz_feeConservation(bool,bool,uint256) (runs: 256, μ: 832085, ~: 833449)\nLogs:\n  Bound result 4132794461\n\n[PASS] test_allFourSwapModesPayExactlyOnePercentOfGrossIMD() (gas: 2249846)\n[PASS] test_allowlistStartsOnFirstSwapAndExpiresAtExactly900Seconds() (gas: 912933)\n[PASS] test_callbacksRejectNonManagerAndForgedUnlock() (gas: 160768)\n[PASS] test_dustAndFeeRoundingBoundariesInAllModes() (gas: 13267588)\n[PASS] test_failedDirectTransferFallsBackForAllSwapModes() (gas: 2945219)\n[PASS] test_freshTokenOnlyPoolMintsClaimsOnExactInputBuy() (gas: 1167244)\n[PASS] test_freshTokenOnlyPoolMintsClaimsOnExactOutputBuy() (gas: 1166568)\n[PASS] test_initializationPermissionsAndSupply() (gas: 125098)\n[PASS] test_multipleSwapsWithinOneUnlockAccumulateClaimsExactly() (gas: 919542)\n[PASS] test_noLiquidityCannotConsumeFeeOrStartWindow() (gas: 1017501)\n[PASS] test_noOwnershipTransferOrOtherAdminSelectors() (gas: 153207)\n[PASS] test_onlyFactoryCanInitializeAndOnlyOnePool() (gas: 273169)\n[PASS] test_onlyOwnerMayChangePauseAndAllowlist() (gas: 191636)\n[PASS] test_partialClaimRedemptionPreservesRemainder() (gas: 1289883)\n[PASS] test_partialSpecifiedIMDBuyRevertsWithoutFeeOrWindow() (gas: 697636)\n[PASS] test_partialSpecifiedIMDSellRevertsWithoutFeeOrWindow() (gas: 700487)\n[PASS] test_partialUnspecifiedIMDUsesOnlyActualTrade() (gas: 1015627)\n[PASS] test_pauseBlocksBothDirectionsButFactoryAndLPsCanExit() (gas: 2147925)\n[PASS] test_pauseDoesNotRestartOrExtendWindow() (gas: 755839)\n[PASS] test_reentrantFeeAndRedemptionCallsCannotSpendClaimsTwice() (gas: 1286873)\n[PASS] test_rejectsWrongPairFeeAndSpacing() (gas: 341910)\n[PASS] test_sellStartsWindowAndDoesNotNeedAllowlist() (gas: 1020663)\n[PASS] test_separateLaunchesHaveIsolatedWindowsPauseAllowlistAndClaims() (gas: 1551297)\n[PASS] test_sharedRelayerUsesOriginAndIgnoresHookData() (gas: 887110)\n[PASS] test_unknownPoolCannotReadOrMutateAnotherPool() (gas: 265931)\n[PASS] test_unrepresentableFeeRevertsWithSafeCastInsteadOfArithmeticOverflow() (gas: 121537)\n[PASS] test_zeroTimestampIsNotAnUnstartedSentinel() (gas: 718726)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 45.03ms (94.88ms CPU time)\n\nRan 1 test for test/HookInvariant.t.sol:HookIMD0InvariantTest\n[PASS]\nHookIMD0InvariantTest invariants:\n[PASS] invariant_allFeesArePaidOrBackedByPoolClaims\n[PASS] invariant_currencyConservationAndTokenSupply\n[PASS] invariant_firstSwapWindowCannotBeReset\n HookIMD0InvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭-------------+--------------------+-------+---------+----------╮\n| Contract    | Selector           | Calls | Reverts | Discards |\n+===============================================================+\n| HookHandler | advance            | 296   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | collectLPFees      | 274   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | redeem             | 290   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | setAllowed         | 304   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | setPause           | 277   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | setTransferFailure | 287   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | trade              | 320   | 0       | 0        |\n╰-------------+--------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 8407333306683603488\n  Bound result 120\n  Bound result 3\n  Bound result 4678635282362930137\n  Bound result 9999999000000001610\n  Bound result 79\n  Bound result 9999999000000019535\n  Bound result 9999999002661172839\n  Bound result 401\n  Bound result 6\n  Bound result 242\n  Bound result 553\n  Bound result 480\n  Bound result 9999999003754307466\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 336.22ms (333.89ms CPU time)\n\nRan 1 test for test/HookInvariant.t.sol:HookIMD1InvariantTest\n[PASS]\nHookIMD1InvariantTest invariants:\n[PASS] invariant_allFeesArePaidOrBackedByPoolClaims\n[PASS] invariant_currencyConservationAndTokenSupply\n[PASS] invariant_firstSwapWindowCannotBeReset\n HookIMD1InvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭-------------+--------------------+-------+---------+----------╮\n| Contract    | Selector           | Calls | Reverts | Discards |\n+===============================================================+\n| HookHandler | advance            | 296   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | collectLPFees      | 274   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | redeem             | 290   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | setAllowed         | 304   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | setPause           | 277   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | setTransferFailure | 287   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | trade              | 320   | 0       | 0        |\n╰-------------+--------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 9999999000000015437\n  Bound result 120\n  Bound result 3\n  Bound result 4678635282362930137\n  Bound result 9999999000000000392\n  Bound result 79\n  Bound result 9999999000000014055\n  Bound result 9999999002661172838\n  Bound result 328\n  Bound result 6\n  Bound result 242\n  Bound result 553\n  Bound result 480\n  Bound result 9999999003754307465\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 356.79ms (354.17ms CPU time)\n\nRan 6 test suites in 358.50ms (821.15ms CPU time): 66 tests passed, 0 failed, 0 skipped (66 total tests)\n","passed":true},{"durationMs":49,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"TestHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"TestHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"TestHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"TestHook.claimFees(bytes32)\",\"TestHook.pause(bytes32)\",\"TestHook.setAllowed(bytes32,address[],bool)\",\"TestHook.unlockCallback(bytes)\",\"TestHook.unpause(bytes32)\",\"TestToken.approve(address,uint256)\",\"TestToken.transfer(address,uint256)\",\"TestToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":3,\"README.md\":124,\"docs/SECURITY.md\":51,\"docs/dependencies.json\":22,\"foundry.toml\":22,\"launch.json\":31,\"remappings.txt\":4,\"src/TestHook.sol\":301,\"src/TestToken.sol\":11,\"test/Deployment.t.sol\":79,\"test/HookInvariant.t.sol\":170,\"test/TestHook.t.sol\":675,\"test/TestToken.t.sol\":97,\"test/helpers/MockIMD.sol\":38,\"test/helpers/PoolActor.sol\":78},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"3f55e5bc59798e487cc3a42cd06a945e92b3b21e23a82c061af2b5b948f7db18","verifiedTreeHash":"8985c2c69bb7e5dfcf84b2d87fa729d077966aa7","verifierVersion":"0.1.0+560ab6a2"},{"checks":[{"durationMs":9216,"exitCode":0,"name":"build","output":"Compiling 79 files with Solc 0.8.26\nSolc 0.8.26 finished in 8.98s\nCompiler run successful!\n","passed":true},{"durationMs":995,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 5 tests for test/TestToken.t.sol:TestTokenTest\n[PASS] testFuzz_plainTransfersAndAllowances(uint256) (runs: 256, μ: 235609, ~: 236037)\nLogs:\n  Bound result 999999999999999999999999999\n\n[PASS] test_factoryAllocationAndContractTransfers() (gas: 1519178)\n[PASS] test_insufficientBalanceAndAllowanceRevert() (gas: 67540)\n[PASS] test_metadataAndFactoryMint() (gas: 1287070)\n[PASS] test_noAdminMintBurnOrUpgradeSelectors() (gas: 286938)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 13.60ms (14.98ms CPU time)\n\nRan 28 tests for test/TestHook.t.sol:TestHookIMD0Test\n[PASS] testFuzz_feeConservation(bool,bool,uint256) (runs: 256, μ: 831703, ~: 829282)\nLogs:\n  Bound result 14264337671979831211\n\n[PASS] test_allFourSwapModesPayExactlyOnePercentOfGrossIMD() (gas: 2250108)\n[PASS] test_allowlistStartsOnFirstSwapAndExpiresAtExactly900Seconds() (gas: 920023)\n[PASS] test_callbacksRejectNonManagerAndForgedUnlock() (gas: 160406)\n[PASS] test_dustAndFeeRoundingBoundariesInAllModes() (gas: 13289666)\n[PASS] test_failedDirectTransferFallsBackForAllSwapModes() (gas: 2945481)\n[PASS] test_freshTokenOnlyPoolMintsClaimsOnExactInputBuy() (gas: 1179502)\n[PASS] test_freshTokenOnlyPoolMintsClaimsOnExactOutputBuy() (gas: 1176937)\n[PASS] test_initializationPermissionsAndSupply() (gas: 125098)\n[PASS] test_multipleSwapsWithinOneUnlockAccumulateClaimsExactly() (gas: 932782)\n[PASS] test_noLiquidityCannotConsumeFeeOrStartWindow() (gas: 1030291)\n[PASS] test_noOwnershipTransferOrOtherAdminSelectors() (gas: 153207)\n[PASS] test_onlyFactoryCanInitializeAndOnlyOnePool() (gas: 273127)\n[PASS] test_onlyOwnerMayChangePauseAndAllowlist() (gas: 191636)\n[PASS] test_partialClaimRedemptionPreservesRemainder() (gas: 1302241)\n[PASS] test_partialSpecifiedIMDBuyRevertsWithoutFeeOrWindow() (gas: 703780)\n[PASS] test_partialSpecifiedIMDSellRevertsWithoutFeeOrWindow() (gas: 694481)\n[PASS] test_partialUnspecifiedIMDUsesOnlyActualTrade() (gas: 1023791)\n[PASS] test_pauseBlocksBothDirectionsButFactoryAndLPsCanExit() (gas: 2147895)\n[PASS] test_pauseDoesNotRestartOrExtendWindow() (gas: 749251)\n[PASS] test_reentrantFeeAndRedemptionCallsCannotSpendClaimsTwice() (gas: 1286821)\n[PASS] test_rejectsWrongPairFeeAndSpacing() (gas: 341814)\n[PASS] test_sellStartsWindowAndDoesNotNeedAllowlist() (gas: 1013474)\n[PASS] test_separateLaunchesHaveIsolatedWindowsPauseAllowlistAndClaims() (gas: 1557815)\n[PASS] test_sharedRelayerUsesOriginAndIgnoresHookData() (gas: 894382)\n[PASS] test_unknownPoolCannotReadOrMutateAnotherPool() (gas: 265931)\n[PASS] test_unrepresentableFeeRevertsWithSafeCastInsteadOfArithmeticOverflow() (gas: 121555)\n[PASS] test_zeroTimestampIsNotAnUnstartedSentinel() (gas: 712138)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 62.98ms (144.06ms CPU time)\n\nRan 3 tests for test/Deployment.t.sol:DeploymentTest\n[PASS] test_constructorAcceptsNonzeroArgumentsWithoutCodeOnEmptyChain() (gas: 50890)\n[PASS] test_constructorRejectsZeroArgumentsAndWrongFlags() (gas: 6949)\n[PASS] test_realCreate2DeploymentFromFactoryHasCorrectOwnerAndFlags() (gas: 51335292)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 66.39ms (66.59ms CPU time)\n\nRan 28 tests for test/TestHook.t.sol:TestHookIMD1Test\n[PASS] testFuzz_feeConservation(bool,bool,uint256) (runs: 256, μ: 832085, ~: 833449)\nLogs:\n  Bound result 14264337671979831211\n\n[PASS] test_allFourSwapModesPayExactlyOnePercentOfGrossIMD() (gas: 2249846)\n[PASS] test_allowlistStartsOnFirstSwapAndExpiresAtExactly900Seconds() (gas: 912933)\n[PASS] test_callbacksRejectNonManagerAndForgedUnlock() (gas: 160768)\n[PASS] test_dustAndFeeRoundingBoundariesInAllModes() (gas: 13267588)\n[PASS] test_failedDirectTransferFallsBackForAllSwapModes() (gas: 2945219)\n[PASS] test_freshTokenOnlyPoolMintsClaimsOnExactInputBuy() (gas: 1167244)\n[PASS] test_freshTokenOnlyPoolMintsClaimsOnExactOutputBuy() (gas: 1166568)\n[PASS] test_initializationPermissionsAndSupply() (gas: 125098)\n[PASS] test_multipleSwapsWithinOneUnlockAccumulateClaimsExactly() (gas: 919542)\n[PASS] test_noLiquidityCannotConsumeFeeOrStartWindow() (gas: 1017501)\n[PASS] test_noOwnershipTransferOrOtherAdminSelectors() (gas: 153207)\n[PASS] test_onlyFactoryCanInitializeAndOnlyOnePool() (gas: 273169)\n[PASS] test_onlyOwnerMayChangePauseAndAllowlist() (gas: 191636)\n[PASS] test_partialClaimRedemptionPreservesRemainder() (gas: 1289883)\n[PASS] test_partialSpecifiedIMDBuyRevertsWithoutFeeOrWindow() (gas: 697636)\n[PASS] test_partialSpecifiedIMDSellRevertsWithoutFeeOrWindow() (gas: 700487)\n[PASS] test_partialUnspecifiedIMDUsesOnlyActualTrade() (gas: 1015627)\n[PASS] test_pauseBlocksBothDirectionsButFactoryAndLPsCanExit() (gas: 2147925)\n[PASS] test_pauseDoesNotRestartOrExtendWindow() (gas: 755839)\n[PASS] test_reentrantFeeAndRedemptionCallsCannotSpendClaimsTwice() (gas: 1286873)\n[PASS] test_rejectsWrongPairFeeAndSpacing() (gas: 341910)\n[PASS] test_sellStartsWindowAndDoesNotNeedAllowlist() (gas: 1020663)\n[PASS] test_separateLaunchesHaveIsolatedWindowsPauseAllowlistAndClaims() (gas: 1551297)\n[PASS] test_sharedRelayerUsesOriginAndIgnoresHookData() (gas: 887110)\n[PASS] test_unknownPoolCannotReadOrMutateAnotherPool() (gas: 265931)\n[PASS] test_unrepresentableFeeRevertsWithSafeCastInsteadOfArithmeticOverflow() (gas: 121537)\n[PASS] test_zeroTimestampIsNotAnUnstartedSentinel() (gas: 718726)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 71.94ms (148.20ms CPU time)\n\nRan 1 test for test/HookInvariant.t.sol:HookIMD0InvariantTest\n[PASS]\nHookIMD0InvariantTest invariants:\n[PASS] invariant_allFeesArePaidOrBackedByPoolClaims\n[PASS] invariant_currencyConservationAndTokenSupply\n[PASS] invariant_firstSwapWindowCannotBeReset\n HookIMD0InvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭-------------+--------------------+-------+---------+----------╮\n| Contract    | Selector           | Calls | Reverts | Discards |\n+===============================================================+\n| HookHandler | advance            | 296   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | collectLPFees      | 274   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | redeem             | 290   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | setAllowed         | 304   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | setPause           | 277   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | setTransferFailure | 287   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | trade              | 320   | 0       | 0        |\n╰-------------+--------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 8407333306683603488\n  Bound result 120\n  Bound result 3\n  Bound result 4678635282362930137\n  Bound result 9999999000000001610\n  Bound result 79\n  Bound result 9999999000000019535\n  Bound result 9999999002661172839\n  Bound result 401\n  Bound result 6\n  Bound result 242\n  Bound result 553\n  Bound result 480\n  Bound result 9999999003754307466\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 781.96ms (777.39ms CPU time)\n\nRan 1 test for test/HookInvariant.t.sol:HookIMD1InvariantTest\n[PASS]\nHookIMD1InvariantTest invariants:\n[PASS] invariant_allFeesArePaidOrBackedByPoolClaims\n[PASS] invariant_currencyConservationAndTokenSupply\n[PASS] invariant_firstSwapWindowCannotBeReset\n HookIMD1InvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭-------------+--------------------+-------+---------+----------╮\n| Contract    | Selector           | Calls | Reverts | Discards |\n+===============================================================+\n| HookHandler | advance            | 296   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | collectLPFees      | 274   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | redeem             | 290   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | setAllowed         | 304   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | setPause           | 277   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | setTransferFailure | 287   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| HookHandler | trade              | 320   | 0       | 0        |\n╰-------------+--------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 9999999000000015437\n  Bound result 120\n  Bound result 3\n  Bound result 4678635282362930137\n  Bound result 9999999000000000392\n  Bound result 79\n  Bound result 9999999000000014055\n  Bound result 9999999002661172838\n  Bound result 328\n  Bound result 6\n  Bound result 242\n  Bound result 553\n  Bound result 480\n  Bound result 9999999003754307465\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 797.98ms (793.18ms CPU time)\n\nRan 6 test suites in 800.99ms (1.79s CPU time): 66 tests passed, 0 failed, 0 skipped (66 total tests)\n","passed":true},{"durationMs":143,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"TestHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"TestHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"TestHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"TestHook.claimFees(bytes32)\",\"TestHook.pause(bytes32)\",\"TestHook.setAllowed(bytes32,address[],bool)\",\"TestHook.unlockCallback(bytes)\",\"TestHook.unpause(bytes32)\",\"TestToken.approve(address,uint256)\",\"TestToken.transfer(address,uint256)\",\"TestToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":3,\"README.md\":124,\"docs/SECURITY.md\":51,\"docs/dependencies.json\":22,\"foundry.toml\":22,\"remappings.txt\":4,\"src/TestHook.sol\":301,\"src/TestToken.sol\":11,\"test/Deployment.t.sol\":79,\"test/HookInvariant.t.sol\":170,\"test/TestHook.t.sol\":675,\"test/TestToken.t.sol\":97,\"test/helpers/MockIMD.sol\":38,\"test/helpers/PoolActor.sol\":78},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":2252,"exitCode":0,"name":"slither","output":"[medium/medium] tx-origin at src/TestHook.sol:183: TestHook.beforeSwap(address,PoolKey,SwapParams,bytes) (src/TestHook.sol#183-202) uses tx.origin for authorization: buying && (! state.started || block.timestamp - state.firstSwapTimestamp < ALLOWLIST_DURATION) && ! state.allowed[tx.origin] (src/TestHook.sol#195-196)\n[low/medium] reentrancy-benign at src/TestHook.sol:241: Reentrancy in TestHook.claimFees(PoolId) (src/TestHook.sol#241-248):\n[low/medium] reentrancy-events at src/TestHook.sol:250: Reentrancy in TestHook.unlockCallback(bytes) (src/TestHook.sol#250-267):\n[low/medium] timestamp at src/TestHook.sol:174: TestHook.allowlistActive(PoolId) (src/TestHook.sol#174-177) uses timestamp for comparisons\n[low/medium] timestamp at src/TestHook.sol:183: TestHook.beforeSwap(address,PoolKey,SwapParams,bytes) (src/TestHook.sol#183-202) uses timestamp for comparisons","passed":true},{"durationMs":601,"exitCode":0,"name":"aderyn","output":"[high] reentrancy-state-change at src/TestHook.sol:245: Reentrancy: State change after external call\n[high] tx-origin-used-for-auth at src/TestHook.sol:194: Use of `tx.origin` for authentication\n[low] centralization-risk at src/TestHook.sol:131: Centralization Risk (3 places)\n[low] costly-loop at src/TestHook.sol:143: Costly operations inside loop\n[low] large-numeric-literal at src/TestHook.sol:26: Large Numeric Literal (2 places)\n[low] non-reentrant-not-first at src/TestHook.sol:111: `nonReentrant` is Not the First Modifier (3 places)\n[low] state-change-without-event at src/TestHook.sol:183: State Change Without Event (2 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"7e0b5394444e47b8eac6c646493b87e02e2692fb1f242532e8de3c01a8c4942b","verifiedTreeHash":"a1c13021b2072460b15164c764f9dfd50694c33a","verifierVersion":"0.1.0+560ab6a2"}]}