{"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":"bf4f5d7a-dffa-470e-a307-591e2bdf8955","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"d0eb0ef9f63fb2689b378b63611f70e977ab001ffdbaa7972f5184e70a40677c","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":"b3554dce1ff4def2000b169c5b5b6959edac6e214728d58bb9a6e8dda9f140d2","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":"2d9b4cc2cc41991fd252a1fc5906f1c599f5abbc20e06cc266f737428142851d","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":"3d4e754176f8428b9fe9885781a7b33a8da518a0ba3d31240684b76b06fc9ae5","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":"5a072abd2be6c8010b9448909fd602a5c1099f5cd3c1111657e7c9f2ea081914","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":"a5abbeb31c773fa5ccf67944a6055f27a35834c5b77f8f9716ad01f9db0475be","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":"c766cb8e9174d2345adbde115f8f64223ffbee1d67d0ec21716bbb62aab6054c","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"a2417343e24aba98ee546ffd06d1aa65456c15c70dbb56ecd3a64642679ad318","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 IMDeezNutz ($DEEZ) on Ethereum mainnet as a custom token, paired with $IMD on Uniswap V4.\n\nToken details\n- Name: IMDeezNutz\n- Symbol: DEEZ\n- Total supply: 1,000,000,000 (1B) with 18 decimals, minted once to the factory\n- No owner, no admin functions, no minting after launch, no pause, no blacklist, no transfer taxes outside the defined trading fee, fully immutable\n- Supply split (standard swarm launch): 10% to the IMD swarm, 88% into the Uniswap V4 pool, 2% to the requester (deployer)\n\nRequester’s 2% allocation\nOf the requester’s 2% (20,000,000 $DEEZ), at least 1% of total supply (10,000,000 $DEEZ) must be used to strengthen liquidity. Preferred methods (choose the cleanest and most permanent):\n- Add it as additional single-sided or balanced liquidity into the $DEEZ/$IMD Uniswap V4 pool and lock it permanently, or\n- Use it to seed a permanent buy wall / deeper liquidity position that benefits holders.\n\nThe remaining portion of the 2% can go to the requester wallet or also be locked into LP. Prioritize making the pool stronger.\n\nCore flywheel – 2% of every trade\nImplement a Uniswap V4 hook (or equivalent fee mechanism) that takes exactly 2% on every buy and every sell.\n\n- All of this 2% (after any mandatory Uniswap protocol or network share) must be accumulated and redistributed to all eligible holders, weighted strictly by their $DEEZ balance.\n- Bigger bags receive a proportionally larger share. There is no top-N cutoff.\n- Eligible holders = every wallet holding $DEEZ except:\n  - The Uniswap V4 pool / LP position\n  - The burn address\n  - The fee collector / reward distributor contracts themselves\n- Rewards should preferably be paid in $IMD (buy $IMD with the collected fees) so holders receive the main ecosystem token. Alternative: buy back $DEEZ. Prefer $IMD if cleanly implementable.\n- Distribution must be permissionless and on-chain. Prefer a claimable or periodic drip system that is gas-efficient and verifiable. Anyone should be able to trigger distribution or claim their share based on balance. No admin control after launch.\n- Make the math transparent and easy to verify.\n\nWebsite\nBuild and publish a very professional, single-page public website for the token (on sites.imd.fun or IPFS + ENS if available).\n\nDesign requirements:\n- Match the visual language, typography, spacing, and overall aesthetic of the main imd.fun site as closely as possible.\n- Use the same dark-mode color palette and professional, clean, minimal style found on imd.fun (deep dark backgrounds, high-contrast text, subtle modern accents). Study the live site and replicate its feel.\n- Layout should feel institutional and trustworthy, not meme-heavy.\n- Must include:\n  - Token name, symbol $DEEZ, and copyable contract address\n  - Clear explanation of the flywheel: “2% of every trade is redistributed to all holders proportional to their balance. Larger holdings receive a larger share.”\n  - How to claim rewards (or link to the claim interface)\n  - Links to the Uniswap pool, Etherscan/Blockscout, and DexScreener\n  - Total supply, circulating supply notes if relevant, and liquidity details\n  - Professional hero section and clean sections (Mechanics, Rewards, Contracts)\n- Generate a simple, clean logo/icon that fits the professional IMD aesthetic (swarm can create this).\n\nAdditional requirements\n- Full swarm audit of all contracts before deployment.\n- Return the token address, hook address, pool details, reward/distributor contract address(es), website URL, and transaction hashes.\n- Deploy as a custom_token / univ4_hook launch so all logic can be custom-built.\n\nLaunch it.\n\nToken name: IMDeezNutz\nToken symbol: DEEZ","parentJobId":null,"planHash":"174cbb22d22e545ac4fe1245c7258387fcd0e412e2f7c82c7bd987838fb5c520","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"bf4f5d7a-dffa-470e-a307-591e2bdf8955","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-2225-imdeeznutz"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51490","feedbackHash":"188dd2b7aafe810f914ce68d0cd5871817f6a51a5ac6b4e830d2027394f3d78a","nodeKey":"audit_economics","submissionHash":"d0eb0ef9f63fb2689b378b63611f70e977ab001ffdbaa7972f5184e70a40677c","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51305","feedbackHash":"a796191ad0cceb9d4d2fa9d19578b23e8e5f122f0bab93e3a18852a0fc686801","nodeKey":"audit_flow","submissionHash":"b3554dce1ff4def2000b169c5b5b6959edac6e214728d58bb9a6e8dda9f140d2","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"53917","feedbackHash":"b21c045e9bf367b9482cb18ed97cd6fd8ea830daf2b5f20a75290bb34e6eb9c9","nodeKey":"audit_judge","submissionHash":"2d9b4cc2cc41991fd252a1fc5906f1c599f5abbc20e06cc266f737428142851d","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"53916","feedbackHash":"aa75dfd72065e69e0b3b20f7d1f9812d22e39653b9360954126cd59c2792f580","nodeKey":"audit_judge","submissionHash":"47761209efb5ff4bf26ca3075967d6903cdf707f4982b378d7587934727e3c3c","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52609","feedbackHash":"a7e9a583bc8152b9f0bdd8e246006dc25e984531c6169db4b07a1e2ab737a5d7","nodeKey":"audit_judge","submissionHash":"1f124d305797664fceeff893af9d7205fd81765ec435645e3386ceb8d5edbee7","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51216","feedbackHash":"cc0df37b48d35865ba021646537e503b17c2a82ab845ecab42be9e6161f6e2e7","nodeKey":"audit_math","submissionHash":"3d4e754176f8428b9fe9885781a7b33a8da518a0ba3d31240684b76b06fc9ae5","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51493","feedbackHash":"3b66dedb28a84f9d0230662537daa32697c7e3adc2023755e5686d9de242d90e","nodeKey":"audit_permissions","submissionHash":"5a072abd2be6c8010b9448909fd602a5c1099f5cd3c1111657e7c9f2ea081914","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51250","feedbackHash":"ac9d6881781541a7852476f52f32253199e6abdec902d290008bb3730d94d79a","nodeKey":"build_contract_project","submissionHash":"a5abbeb31c773fa5ccf67944a6055f27a35834c5b77f8f9716ad01f9db0475be","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51239","feedbackHash":"4d0361b446a1efb6486f3b8643e44cedffadc63589dfb3750d581cf501b8a538","nodeKey":"build_contract_project","submissionHash":"802ff802c001e6a15b56463b7e947d847ee9be7cce43caaeb48f0891470291af","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51023","feedbackHash":"55e5beb135a01d6b5237ff809107dda7d4988d2fead3ac7128e978bf3c81f0da","nodeKey":"build_contract_project","submissionHash":"2be9a13e7c5101e051489bd2efb88b8bd225fa99f8ef0318e1e335203f091358","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51154","feedbackHash":"fdf7c710b85bd1832c9da0f1798477df67efc18a6424ad06de322061a970ac2c","nodeKey":"manifest","submissionHash":"c766cb8e9174d2345adbde115f8f64223ffbee1d67d0ec21716bbb62aab6054c","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52138","feedbackHash":"9ccf3cb9ae0dec7bc23374203fd9855433fc4c539e1ca860747b6bfee867c85d","nodeKey":"manifest","submissionHash":"15dad5cc14726e959f7b3a6a3b8e4567e76cf899d39cee2d2f229e55830a4df6","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51566","feedbackHash":"aafb82d06af6ec036caf0fe14b62e014abe7a23f2ad367f30b5db8ec9e89fbf4","nodeKey":"manifest","submissionHash":"6ee3f5142d3b76497897138a1b045c55dc3436e7146c1990fc60b706c8aecc3b","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52207","feedbackHash":"5d96e3ce4b52cba93e824cb7ec77db2418063ed2ca40a28c7a42afb3d3322bc2","nodeKey":"write_foundry_tests","submissionHash":"d571eca43de6a6261cebe6e76ca73273e730406f579904197bedb346d6fa2218","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51877","feedbackHash":"ed9fd45a0995add99d9217aec5059a4aa4f7a35bc2e4e0d8735e4ba73a97579d","nodeKey":"write_foundry_tests","submissionHash":"4c99ffd5de5c9b86b3558de43b1162a18d4d8277cfa7b9e8c533f5349a0fd832","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51874","feedbackHash":"baf715487f40f0d1eec0b7b743d698f0734ead9270e5a4ef3b9b2e9796e333c4","nodeKey":"write_foundry_tests","submissionHash":"a2417343e24aba98ee546ffd06d1aa65456c15c70dbb56ecd3a64642679ad318","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"6877759764fba5aeca47bc1e7633d6a7f07f2e0fb6403aa1371d72822365364b","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"35fa2d86771f986553d424b27ee7cc4db154e6862ef15baf10feb9eaa54f5e9d","device":"62491201fe455e89","findings":[],"hash":"15dad5cc14726e959f7b3a6a3b8e4567e76cf899d39cee2d2f229e55830a4df6","nodeId":"2af88f07-4b54-4956-8669-a894d6db9995","outcome":"completed","summary":"Done. `launch.json` is written and is the only file changed. The build succeeds and all 80 tests pass with the verifier's fuzz seed.\n\nWhat the manifest declares, taken from the accepted source:\n\n- **Kind** is `univ4_hook`, written as the first field.\n- **Hook** is `DeezHook` with three constructor arguments in declaration order: `$poolManager`, `$token`, and the IMD pair token address. The hook has no owner argument, since the launch has no owner or admin anywhere.\n- **Permissions** are beforeInitialize, beforeSwap, afterSwap, beforeSwapReturnDelta and afterSwapReturnDelta, exactly what `getHookPermissions` in `src/DeezHook.sol:100` returns.\n- **Token** is `DeezToken`, IMDeezNutz, DEEZ, 18 decimals.\n- **Pool** uses the launch facts verbatim: IMD as paired currency, fee 12500, tick spacing 60, the one-to-one initial price string.\n- **Notes** run under 3,000 characters and explain the 2% IMD-side fee held as ERC-6909 claims, the permissionless harvest, the balance-weighted daily-epoch distributor, the permanent liquidity lock, and that the hook accepts the listed LP fee tier.\n\nOne point worth flagging for reviewers: the third constructor argument is a literal address rather than a placeholder. It is the pair token the launch facts name, not a stand-in, and the hook's constructor rejects the zero address for it.","treeHash":"16f075a68b2d4dfc6c88005c913601085e26801b","usage":{"cachedInputTokens":284628,"inputTokens":162,"model":"claude-fable-5-1","outputTokens":3588,"runtime":"claude","turns":6,"wallClockMs":136778}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"8e25456c4ae69acf","findings":[{"citation":"resolved","description":"Introduced by the round-1 fix for harvest timing (merges write_foundry_tests' finding 253e88be). DeezHook.afterSwap records each fee in pendingFeesOf[epoch]; harvest() (src/DeezHook.sol:197-203) credits the current AND previous epoch, and harvestEpochs() any older one, through DeezRewards.credit(epoch, amount), which adds to pots[epoch].total even after the epoch ended and after holders claimed it. DeezRewards.claim does not harvest first; it pays pot.total * weight / supply as the pot stands and sets claimed[epoch][account] = true, so a later claim reverts AlreadyClaimed(epoch, account). Every fee traded in epoch e after the last harvest of e is thus lost to whoever claims e before the next harvest(); their portion stays in pot e (later claimers' _share is computed on the larger total, so it is not spread to them either), and recycle(e) after CLAIM_WINDOW = 90 epochs pays it to a later epoch's holders. Holders who claim promptly at the start of a day are the ones penalised, so payouts depend on claim timing rather than strictly on balance held, against the brief's 'weighted strictly by their $DEEZ balance'. Funds go to the wrong party only when someone claims before a harvest, hence medium. Fix: have claim() call the hook to harvest the epochs it pays (e.g. hook.harvestEpochs(epochs) before the loop, the hook being immutable and known), or record the amount paid per account per epoch and pay the difference against the current pot.total on a later claim.","line":206,"path":"src/DeezRewards.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 {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.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 {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 {TickMath} from \"v4-core/src/libraries/TickMath.sol\";\nimport {DeezToken} from \"src/DeezToken.sol\";\nimport {DeezHook} from \"src/DeezHook.sol\";\nimport {DeezRewards} from \"src/DeezRewards.sol\";\n\ncontract ScratchIMD is ERC20 {\n    constructor() ERC20(\"IMD\", \"IMD\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\n/// @dev Minimal router paying from its own balance; lets the caller pick the price limit.\ncontract ScratchRouter is IUnlockCallback {\n    IPoolManager public immutable manager;\n\n    constructor(IPoolManager m) {\n        manager = m;\n    }\n\n    function swap(PoolKey memory key, bool zeroForOne, int256 amountSpecified, uint160 limit)\n        external\n        returns (BalanceDelta)\n    {\n        return abi.decode(manager.unlock(abi.encode(uint8(1), key, SwapParams(zeroForOne, amountSpecified, limit))), (BalanceDelta));\n    }\n\n    function addLiquidity(PoolKey memory key, int24 lower, int24 upper, int256 liquidity) external {\n        manager.unlock(abi.encode(uint8(2), key, lower, upper, liquidity));\n    }\n\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        require(msg.sender == address(manager));\n        uint8 kind = abi.decode(data, (uint8));\n        PoolKey memory key;\n        BalanceDelta delta;\n        if (kind == 1) {\n            SwapParams memory p;\n            (, key, p) = abi.decode(data, (uint8, PoolKey, SwapParams));\n            delta = manager.swap(key, p, \"\");\n        } else {\n            int24 lower;\n            int24 upper;\n            int256 liq;\n            (, key, lower, upper, liq) = abi.decode(data, (uint8, PoolKey, int24, int24, int256));\n            (delta,) = manager.modifyLiquidity(key, ModifyLiquidityParams(lower, upper, liq, bytes32(0)), \"\");\n        }\n        _settle(key.currency0, delta.amount0());\n        _settle(key.currency1, delta.amount1());\n        return abi.encode(delta);\n    }\n\n    function _settle(Currency c, int128 amount) private {\n        if (amount < 0) {\n            manager.sync(c);\n            IERC20(Currency.unwrap(c)).transfer(address(manager), uint256(uint128(-amount)));\n            manager.settle();\n        } else if (amount > 0) {\n            manager.take(c, address(this), uint256(uint128(amount)));\n        }\n    }\n}\n\ncontract LateHarvestProof is Test {\n    uint160 constant FLAGS = (1 << 13) | (1 << 7) | (1 << 6) | (1 << 3) | (1 << 2);\n    uint160 constant SQRT_PRICE_1_1 = 79228162514264337593543950336;\n    int24 constant SPACING = 60;\n\n    PoolManager manager;\n    DeezToken token;\n    ScratchIMD imd;\n    DeezHook hook;\n    ScratchRouter router;\n    PoolKey key;\n    bool tokenIsCurrency0;\n\n    function setUp() public {\n        vm.warp(START * 1 days);\n        manager = new PoolManager(address(this));\n        token = new DeezToken();\n        imd = new ScratchIMD();\n        tokenIsCurrency0 = address(token) < address(imd);\n\n        bytes memory creation = abi.encodePacked(\n            type(DeezHook).creationCode, abi.encode(IPoolManager(address(manager)), address(token), address(imd))\n        );\n        bytes32 h = keccak256(creation);\n        for (uint256 i = 0; i < 500_000; i++) {\n            address p = address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), bytes32(i), h)))));\n            if ((uint160(p) & ((1 << 14) - 1)) == FLAGS && p.code.length == 0) {\n                hook = new DeezHook{salt: bytes32(i)}(IPoolManager(address(manager)), address(token), address(imd));\n                break;\n            }\n        }\n        require(address(hook) != address(0), \"no salt\");\n\n        (Currency c0, Currency c1) = tokenIsCurrency0\n            ? (Currency.wrap(address(token)), Currency.wrap(address(imd)))\n            : (Currency.wrap(address(imd)), Currency.wrap(address(token)));\n        key = PoolKey(c0, c1, 12_500, SPACING, IHooks(address(hook)));\n        manager.initialize(key, SQRT_PRICE_1_1);\n\n        router = new ScratchRouter(manager);\n        // The launch pool holds $DEEZ only, in the range just above the opening price.\n        token.transfer(address(router), 100_000_000 ether);\n        token.transfer(alice, 75_000_000 ether);\n        token.transfer(bob, 25_000_000 ether);\n        token.transfer(address(manager), token.balanceOf(address(this)));\n        (int24 lower, int24 upper) = tokenIsCurrency0\n            ? (SPACING, TickMath.maxUsableTick(SPACING))\n            : (TickMath.minUsableTick(SPACING), -SPACING);\n        router.addLiquidity(key, lower, upper, 1e24);\n        imd.mint(address(router), 1_000_000 ether);\n    }\n\n\n    uint256 constant START = 20_000;\n    address alice = address(0xA11CE);\n    address bob = address(0xB0B);\n\n    function buy(uint256 amountIn) internal {\n        bool zeroForOne = !tokenIsCurrency0;\n        uint160 limit = zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1;\n        router.swap(key, zeroForOne, -int256(amountIn), limit);\n    }\n\n    function claimOne(address account, uint256 epoch) internal returns (uint256 paid) {\n        uint256[] memory list = new uint256[](1);\n        list[0] = epoch;\n        DeezRewards rewards = hook.rewards();\n        vm.prank(account);\n        try rewards.claim(account, list) returns (uint256 p) { paid = p; } catch {}\n    }\n\n    /// Fees of epoch START traded after the last harvest are credited to epoch START by the next harvest(),\n    /// but a holder who already claimed START (bool `claimed`) can never collect its share of them.\n    function test_earlyClaimerLosesFeesHarvestedIntoTheEpochLater() public {\n        DeezRewards rewards = hook.rewards();\n        vm.warp(START * 1 days + 12 hours);\n        buy(5_000 ether); // fee 100 IMD\n        hook.harvest();\n        vm.warp(START * 1 days + 23 hours);\n        buy(5_000 ether); // another 100 IMD, traded in epoch START, still pending in the hook\n        assertEq(hook.pendingFeesOf(START), 100 ether);\n\n        vm.warp((START + 1) * 1 days); // first second of the next epoch\n        uint256 first = claimOne(alice, START);\n        hook.harvest(); // credits the pending 100 IMD to epoch START, as designed\n        (uint128 total,,,) = rewards.pots(START);\n        assertEq(uint256(total), 200 ether);\n\n        uint256 owed = uint256(total) * rewards.weightOf(alice, START) / rewards.eligibleSupplyAt(START);\n        uint256 second = claimOne(alice, START); // reverts AlreadyClaimed: caught, pays 0\n        emit log_named_uint(\"alice first claim\", first);\n        emit log_named_uint(\"alice second claim\", second);\n        emit log_named_uint(\"alice owed for the epoch\", owed);\n        assertApproxEqAbs(first + second, owed, 1e6, \"early claimer must receive its share of every fee of the epoch\");\n    }\n}","reproduction":"test/scratch/LateHarvest.t.sol (the proof below; fresh PoolManager, DeezToken, mock IMD, DeezHook mined onto 0x20CC, DEEZ-only launch liquidity; alice 75M and bob 25M DEEZ held for the whole epoch START). At START*1d+12h buy 5,000 IMD exact input (fee 100 IMD), hook.harvest(); at START*1d+23h buy 5,000 IMD again (fee 100 IMD, pendingFeesOf(START) = 100e18). At (START+1)*1d alice claims [START] and gets 37.687e18 IMD (her average-balance share of the 100 IMD pot then). hook.harvest() then credits the remaining 100 IMD to START: pots(START).total = 200e18, and alice's share is 200e18 * weightOf(alice) / eligibleSupplyAt = 75.375e18. Her second claim reverts AlreadyClaimed. Expected: alice receives 75.375e18 in total; actual: 37.687e18, the other 37.687e18 waits 90 epochs and is recycled to other holders. The proof fails on this code with 'early claimer must receive its share of every fee of the epoch: 37687384265169173167 !~= 75374768530338346334'; it passes once claim() harvests first or pays the difference on a second claim. write_foundry_tests' Proof_253e88be835a (rewards-only, the test plays the hook) fails the same way: 75e18 != 150e18.","severity":"medium","snippet":"            claimed[epoch][account] = true;","title":"A holder who claims an epoch before its last fees are harvested permanently loses its share of them: harvest() credits fees to ended epochs after claims, but claimed[epoch][account] is a one-shot bool"}],"hash":"1f124d305797664fceeff893af9d7205fd81765ec435645e3386ceb8d5edbee7","nodeId":"c32d4281-49db-43c5-a91e-6b5f5c54b6c4","outcome":"completed","summary":"I've written the review to `.imd-findings.json`. Three of my four earlier findings are fixed and I dropped the fourth (the 1% lock not being enforced on-chain). I'm reporting one new medium-severity defect that the author's last fix introduced.\n\n**My earlier findings**\n- **High: the 2% fee was charged on the requested amount, not what actually traded.** Fixed. If the pool fills only part of a swap where $IMD is the specified amount, `afterSwap` now reverts with `PartialFill`. Both of my earlier proofs pass. The permissions specialist's proof (`Proof_abe5a880a30d`) still fails, but only because it treats the new revert as a failure; that revert is the fix.\n- **High: anyone could claim on behalf of a contract and strand its $IMD there.** Fixed. `claim` now requires the caller to be the account itself, and the proof passes. The claim window is down to 90 days, so a share nobody claims (such as the swarm distributor's 10%) goes back to holders sooner.\n- **Medium: a wallet could hold for one block across midnight UTC and take a full share of the day's fees.** Fixed. Each fee is now recorded against the day it was traded, and rewards are weighted by average balance over the day. I traced how the token tracks balances over time and found no rounding or overflow problems.\n- **Low: anyone could send 1 wei of $IMD to the liquidity lock to spoil a single-sided lock.** Fixed. `lockAs` lets the caller name the mode and a tick range, and ignores the other token.\n- **Info: nothing on-chain forces the requester to lock 1% of supply.** Dropped. The author is right that this is a limit of how the factory deploys the launch, not a code defect, and the README says so.\n\n**New finding (medium), at `src/DeezRewards.sol:206`, `claimed[epoch][account] = true;`**\n\nA holder who claims a day's rewards early loses their share of any fees from that day that were collected afterwards.\n- `harvest()` can add fees to a day that has already ended, but `claim()` marks each holder as paid for that day only once.\n- So anyone who claims before the last harvest can never collect their share of the late fees. That share then waits 90 days and is recycled to other holders.\n- **Reproduction with the real hook:** fees of 100 $IMD were harvested at midday, and another 100 $IMD were traded later the same day. Alice claims at the first second of the next day, then `harvest()` adds the second 100. Alice receives 37.69 $IMD but is owed 75.37, and her second claim reverts with `AlreadyClaimed`.\n- My proof fails on the current code for exactly that reason. The specialist's simpler proof, which plays the hook itself, fails the same way (75 against an expected 150).\n- **Fix:** have `claim()` harvest the days it pays first, or record how much each account was paid per day and pay the difference on a later claim.\n\n**Other checks**\n- None of the static-analysis warnings turned out to be a real problem.\n- The contracts are within the deployment size limits; the hook's creation code is 26,568 bytes and its deployed code 8,731.\n- All of the project's own test suites pass.\n\nThe coverage record has a row for each of the 16 entry points: 12 behave as intended. `harvest`, `harvestEpochs`, `claim` and `credit` are tied to the new finding. I removed my scratch tests, so no repository files are changed.","treeHash":null,"usage":{"cachedInputTokens":1069439,"inputTokens":24,"model":"claude-opus-5-5","outputTokens":14972,"runtime":"claude","turns":15,"wallClockMs":288368}},{"artifacts":[],"attempt":1,"bundleHash":"a8a99c5ab289ad9cf64ffad60ba7884b1af7f1ba36e4a26bec72d14eae735abc","device":"ca080fd306399669","findings":[],"hash":"2be9a13e7c5101e051489bd2efb88b8bd225fa99f8ef0318e1e335203f091358","nodeId":"d3e365b3-3ffa-43a2-aec2-31360ff75da0","outcome":"completed","summary":"All work is done and verified. Here is the recap.\n\n**Outcome.** All five findings were reproduced and answered in `.imd-responses.json`. Four are fixed, one is disputed. Both reviewer proofs now pass. The full suite passes with the verifier's fuzz seed, and `forge build` and `forge fmt --check` are clean.\n\n| Check | Result |\n| --- | --- |\n| Own tests in `test/` | 113 passed |\n| Reviewer proofs | 3 passed |\n| Hook and token floor rehearsal | 10 passed, 1 skipped (needs the verifier's factory probe) |\n\n**What changed, per finding**\n\n- **Fee on partial fills (high, fixed).** When $IMD is the specified side, `afterSwap` now compares the realised $IMD delta with what `beforeSwap` assumed and reverts the swap with `PartialFill` on any mismatch. Nothing is charged. The $DEEZ-specified cases already read the realised delta. The afterSwap return delta can only touch the unspecified currency, so refunding the overcharge was not expressible there. Refusing the fill is what the proof accepts.\n- **Third-party claims (high, fixed).** `claim` now requires the caller to be the account. The claim window was shortened from 365 to 90 epochs so shares of contracts that cannot claim return to holders within a quarter. I did not add the suggested factory lookup of the swarm distributor, because no interface for it is given in the inputs and a wrong selector would brick every claim in an ownerless contract.\n- **Harvest-timed epochs and one-second snapshots (medium, fixed).** The hook records each fee against the epoch it was traded in and credits it to that epoch's pot through a new hook-only `credit` function, so harvest timing chooses nothing. The token's checkpoints now also store a cumulative balance in wei-days, still one storage slot each, and rewards are weighted by the average balance over the epoch. A one-second holder earns one 86,400th of its bag's weight. A new end-to-end test replays the reviewer's snipe scenario.\n- **Lock mode from mutable state (low, fixed).** New `lockAs(mode, minTick, maxTick)` with explicit single-sided, full-range and auto modes plus a tick bound. The 1 wei grief now locks the full 10,000,000 $DEEZ single-sided. `lock()` remains as the convenience form.\n- **1% lock not enforced on chain (info, disputed).** Accurate as a description, but a constraint of the launch model: the factory pays the 2% to the requester and no contract here may receive supply at launch. The README states this plainly and the lock is verifiable afterwards.\n\n**Also updated.** The README documents the partial-fill rule, per-epoch harvesting, the average-balance math, the new lock modes, the new constants, and a revision history. The site text now describes self-claims, the 90-day window, time-weighted shares, and `lockAs`. The Owners floor suite could not be rehearsed here because it imports a helper only the verifier supplies.","treeHash":"f098df227db06968d7e1267b63e91c87551383b3","usage":{"cachedInputTokens":5363901,"inputTokens":962,"model":"claude-fable-5-1","outputTokens":83518,"runtime":"claude","turns":67,"wallClockMs":1364203}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"1eba9b0f789fcc77","findings":[],"hash":"2d9b4cc2cc41991fd252a1fc5906f1c599f5abbc20e06cc266f737428142851d","nodeId":"c32d4281-49db-43c5-a91e-6b5f5c54b6c4","outcome":"completed","summary":"The review is complete. The findings file at `.imd-findings.json` holds zero findings and 18 coverage rows, and no tracked file was changed.\n\n**My round-1 finding is fixed.** The proof for the early-claimer loss, copied under test/scratch and run against this tree, passes. In `src/DeezRewards.sol` the claim path now calls the hook's `harvestEpochs` for the epochs it pays before computing shares, and the one-shot claimed flag is replaced by a paid-amount map so a repeat claim pays the difference against a grown pot. The author's end-to-end test reproduces my exact scenario and pays alice her full share in one claim.\n\n**Specialist findings all describe the pre-fix code and no longer reproduce.**\n- Partial-fill overcharge (audit_permissions, audit_flow, audit_math): the hook now compares the pool's paired delta with the up-front fee and reverts with `PartialFill`. The math proof passes. The permissions proof fails only because its swap now reverts with that error rather than overcharging, which is the designed outcome.\n- Claim on behalf of a contract holder (audit_economics, audit_flow): claim requires the caller to be the account. The economics proof passes.\n- Epoch-boundary sniping (audit_economics, audit_flow): weights are now the average balance over the epoch from the token's cumulative checkpoints, so a one-block holder weighs a fraction of a day.\n- Lock griefed by one wei (audit_flow, audit_math): an explicit `lockAs` mode zeroes the other side and takes a tick bound. The convenience `lock()` still chooses by balance, but nothing can leave the contract and the README directs the 1% lock to the explicit form.\n\n**Nothing new to report.** I traced every listed entry point, re-checked delta accounting on all four swap shapes, the credit and recycle paths, the checkpoint casts, and verified on chain that $IMD is a plain OpenZeppelin ERC-20 under a LayerZero OFT with no transfer fee or pause, so harvest and credit cannot be starved. The project suite passes with all 157 tests. The swarm's Merkle distributor remains an eligible holder, as the brief's exclusion list allows, and its share is recycled after the claim window rather than lost.","treeHash":null,"usage":{"cachedInputTokens":900139,"inputTokens":290,"model":"claude-fable-5-1","outputTokens":17760,"runtime":"claude","turns":16,"wallClockMs":289733}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"fa2b7fcb5f53535a","findings":[{"citation":"resolved","description":"When $IMD is the specified currency (exact-input buy, exact-output sell) the fee is derived purely from params.amountSpecified in beforeSwap and minted again from the same number in afterSwap (_specifiedFee at src/DeezHook.sol:167). The pool, however, may fill only part of amountToSwap: v4 stops at the trader's sqrtPriceLimitX96 or when it runs out of liquidity in that direction and returns a smaller BalanceDelta without reverting. The hook never compares the pool's actual specified-side delta with the request, so the trader is charged 2% (or 2/98) of what it asked for rather than of what changed hands. Boundary x precision seam: the formula is exact in the interior of the domain (full fill) and wrong at the edge (partial or zero fill). Two concrete consequences. (1) On the launch pool, which holds $DEEZ only (no $IMD liquidity below the price), an exact-output sell of 1000 $IMD fills nothing: swapDelta is (0,0), yet the hook's beforeSwap delta of +20.408 $IMD is subtracted from the swapper's delta, so the swapper's $IMD delta becomes -20.408e18 and it pays 20.408 $IMD for receiving nothing. The contract's own invariant 'the trader receives exactly what it asked for' (comment at line 28) is violated and the swap net-pays the hook in the currency the trader was supposed to receive. (2) An exact-input buy of 1000 $IMD with a price limit (or thin liquidity) that lets the pool take only 253.94 $IMD is still charged 20 $IMD: 7.3% of the gross instead of 2%. Through the Universal Router case (1) and large partial exact-output fills revert on TAKE_ALL (negative or short credit), so the user-visible effect there is a failed sell; any integrator settling deltas directly (bots, aggregators, the project's own Swapper-style router) pays the overcharge. The unspecified-side cases (_unspecifiedFee) read the real delta and are correct. Fix: in afterSwap, when $IMD is the specified side, read the pool's specified-side delta (delta.amount0()/amount1() on the $IMD side) and require it to equal amountSpecified +/- fee (full fill), reverting otherwise; or compute the fee from the filled specified amount and refund the difference to the swapper via a negative unspecified delta is not possible, so reverting on partial fills is the minimal safe change.","line":204,"path":"src/DeezHook.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 {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.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 {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 {TickMath} from \"v4-core/src/libraries/TickMath.sol\";\nimport {DeezToken} from \"src/DeezToken.sol\";\nimport {DeezHook} from \"src/DeezHook.sol\";\n\ncontract ScratchIMD is ERC20 {\n    constructor() ERC20(\"IMD\", \"IMD\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\n/// @dev Minimal router paying from its own balance; lets the caller pick the price limit.\ncontract ScratchRouter is IUnlockCallback {\n    IPoolManager public immutable manager;\n\n    constructor(IPoolManager m) {\n        manager = m;\n    }\n\n    function swap(PoolKey memory key, bool zeroForOne, int256 amountSpecified, uint160 limit)\n        external\n        returns (BalanceDelta)\n    {\n        return abi.decode(manager.unlock(abi.encode(uint8(1), key, SwapParams(zeroForOne, amountSpecified, limit))), (BalanceDelta));\n    }\n\n    function addLiquidity(PoolKey memory key, int24 lower, int24 upper, int256 liquidity) external {\n        manager.unlock(abi.encode(uint8(2), key, lower, upper, liquidity));\n    }\n\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        require(msg.sender == address(manager));\n        uint8 kind = abi.decode(data, (uint8));\n        PoolKey memory key;\n        BalanceDelta delta;\n        if (kind == 1) {\n            SwapParams memory p;\n            (, key, p) = abi.decode(data, (uint8, PoolKey, SwapParams));\n            delta = manager.swap(key, p, \"\");\n        } else {\n            int24 lower;\n            int24 upper;\n            int256 liq;\n            (, key, lower, upper, liq) = abi.decode(data, (uint8, PoolKey, int24, int24, int256));\n            (delta,) = manager.modifyLiquidity(key, ModifyLiquidityParams(lower, upper, liq, bytes32(0)), \"\");\n        }\n        _settle(key.currency0, delta.amount0());\n        _settle(key.currency1, delta.amount1());\n        return abi.encode(delta);\n    }\n\n    function _settle(Currency c, int128 amount) private {\n        if (amount < 0) {\n            manager.sync(c);\n            IERC20(Currency.unwrap(c)).transfer(address(manager), uint256(uint128(-amount)));\n            manager.settle();\n        } else if (amount > 0) {\n            manager.take(c, address(this), uint256(uint128(amount)));\n        }\n    }\n}\n\ncontract FeeOnRequestedAmountTest is Test {\n    uint160 constant FLAGS = (1 << 13) | (1 << 7) | (1 << 6) | (1 << 3) | (1 << 2);\n    uint160 constant SQRT_PRICE_1_1 = 79228162514264337593543950336;\n    int24 constant SPACING = 60;\n\n    PoolManager manager;\n    DeezToken token;\n    ScratchIMD imd;\n    DeezHook hook;\n    ScratchRouter router;\n    PoolKey key;\n    bool tokenIsCurrency0;\n\n    function setUp() public {\n        manager = new PoolManager(address(this));\n        token = new DeezToken();\n        imd = new ScratchIMD();\n        tokenIsCurrency0 = address(token) < address(imd);\n\n        bytes memory creation = abi.encodePacked(\n            type(DeezHook).creationCode, abi.encode(IPoolManager(address(manager)), address(token), address(imd))\n        );\n        bytes32 h = keccak256(creation);\n        for (uint256 i = 0; i < 500_000; i++) {\n            address p = address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), bytes32(i), h)))));\n            if ((uint160(p) & ((1 << 14) - 1)) == FLAGS && p.code.length == 0) {\n                hook = new DeezHook{salt: bytes32(i)}(IPoolManager(address(manager)), address(token), address(imd));\n                break;\n            }\n        }\n        require(address(hook) != address(0), \"no salt\");\n\n        (Currency c0, Currency c1) = tokenIsCurrency0\n            ? (Currency.wrap(address(token)), Currency.wrap(address(imd)))\n            : (Currency.wrap(address(imd)), Currency.wrap(address(token)));\n        key = PoolKey(c0, c1, 12_500, SPACING, IHooks(address(hook)));\n        manager.initialize(key, SQRT_PRICE_1_1);\n\n        router = new ScratchRouter(manager);\n        // The launch pool holds $DEEZ only, in the range just above the opening price.\n        token.transfer(address(router), 100_000_000 ether);\n        (int24 lower, int24 upper) = tokenIsCurrency0\n            ? (SPACING, TickMath.maxUsableTick(SPACING))\n            : (TickMath.minUsableTick(SPACING), -SPACING);\n        router.addLiquidity(key, lower, upper, 1e24);\n        imd.mint(address(router), 1_000_000 ether);\n    }\n\n    /// Exact-output sell of 1000 $IMD on a pool with no $IMD liquidity: the pool fills nothing, but the hook still\n    /// charges 2/98 of the requested 1000 $IMD. The trader pays ~20.41 $IMD and receives nothing.\n    function test_exactOutputSellThatFillsNothingStillChargesTheFee() public {\n        uint256 imdBefore = imd.balanceOf(address(router));\n        uint256 deezBefore = token.balanceOf(address(router));\n\n        // sell = token in, IMD out: zeroForOne when token is currency0. Full price limit, as routers set it.\n        bool zeroForOne = tokenIsCurrency0;\n        uint160 limit = zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1;\n        // A fix that refuses partial fills is acceptable: a revert here is a pass.\n        try router.swap(key, zeroForOne, int256(1_000 ether), limit) {} catch { return; }\n\n        uint256 imdPaid = imdBefore - imd.balanceOf(address(router));\n        uint256 deezPaid = deezBefore - token.balanceOf(address(router));\n        emit log_named_uint(\"DEEZ paid\", deezPaid);\n        emit log_named_uint(\"IMD paid (should be 0: nothing was received)\", imdPaid);\n        emit log_named_uint(\"hook pendingFees\", hook.pendingFees());\n\n        assertEq(deezPaid, 0, \"pool swapped nothing\");\n        // A fee on a swap that moved nothing is wrong: the trader must not pay IMD on a sell that returned none.\n        assertEq(imdPaid, 0, \"trader charged a fee although no IMD was received\");\n        assertEq(hook.pendingFees(), 0);\n    }\n\n    /// Exact-input buy of 1000 $IMD with a price limit just above the opening price: the pool takes only part of\n    /// the input, but the hook charges 2% of the full requested 1000 $IMD.\n    function test_exactInputBuyPartialFillIsChargedOnTheRequestedInput() public {\n        uint256 imdBefore = imd.balanceOf(address(router));\n        bool zeroForOne = !tokenIsCurrency0; // IMD in, token out\n        // Limit: the price right after the first liquidity tick, so the swap fills only a sliver.\n        int24 limitTick = tokenIsCurrency0 ? int24(SPACING + 5) : int24(-SPACING - 5);\n        uint160 limit = TickMath.getSqrtPriceAtTick(limitTick);\n        // A fix that refuses partial fills is acceptable: a revert here is a pass.\n        try router.swap(key, zeroForOne, -int256(1_000 ether), limit) {} catch { return; }\n        uint256 imdPaid = imdBefore - imd.balanceOf(address(router));\n        uint256 fee = hook.pendingFees();\n        emit log_named_uint(\"IMD paid total\", imdPaid);\n        emit log_named_uint(\"IMD to the pool\", imdPaid - fee);\n        emit log_named_uint(\"hook fee\", fee);\n        // The fee must be 2% of what the trader actually paid in total (1 wei of rounding allowed).\n        assertApproxEqAbs(fee, imdPaid * 200 / 10_000, 1, \"fee is not 2% of the gross IMD actually paid\");\n    }\n}","reproduction":"Fresh PoolManager, DeezToken, mock $IMD, DeezHook mined on flags 0x20CC, pool initialised at sqrtPrice 2^96 with $DEEZ-only liquidity in [60, maxUsableTick] (as the launch does). (a) Router swaps zeroForOne = tokenIsCurrency0, amountSpecified = +1000e18 (exact-output sell of 1000 $IMD), limit MIN_SQRT_PRICE+1. Expected: either revert or no fee, since no $IMD is received and no $DEEZ is taken. Actual: DEEZ paid = 0, router's $IMD balance drops by 20408163265306122448, hook.pendingFees() = 20408163265306122448. (b) Router swaps exact-input buy amountSpecified = -1000e18 with sqrtPriceLimit at tick 65: pool takes 253944197631148456305 $IMD, hook charges 20e18. Expected fee 2% of gross paid (273.94e18) = 5.478e18; actual 20e18 (7.3%). test/scratch/FeeOnRequestedAmount.t.sol fails on both assertions with exactly these numbers.","severity":"medium","snippet":"        if (params.amountSpecified < 0) {\n            // Exact-input buy: 2% of the $IMD paid in.\n            return uint256(-params.amountSpecified) * FEE_BPS / BPS;\n        }\n        // Exact-output sell: the pool pays out 2/98 more than asked, and that extra is the 2% of the gross.\n        return uint256(params.amountSpecified) * FEE_BPS / (BPS - FEE_BPS);","title":"Hook fee on the specified $IMD side is computed from the requested amount, not the filled amount: partial fills are overcharged and an unfilled exact-output sell still costs the trader 2/98 of the req"},{"citation":"resolved","description":"The one-sided branches run only when the other balance is exactly zero. The both-balances path sizes the position as min(l0, l1) over the full range, so any dust of the second token sets the liquidity. Because lock() is permissionless and the lock's balances are public, anyone can front-run the requester's lock() of the 10,000,000 $DEEZ (the brief's mandatory 1%) by transferring 1 wei of $IMD to the lock: lock() then succeeds with liquidity 1, consumes 1 wei of each token, and the 9,999,999.999999999999999999 $DEEZ stay in the contract. Each retry can be griefed again for 1 wei plus gas. Nothing is lost (the tokens can only ever go into the pool) and a private relay avoids the front-run, so this is a liveness/griefing issue, but it means the sentinel branch the comment describes ('$DEEZ only: a position just above the current price') cannot be relied on in a public mempool, and the recorded totalLocked0/totalLocked1 become misleading (1 and 1). Boundary: the external balanceOf reads at lines 68-69; assumption: a one-sided lock sees exactly zero on the other side; actual: a 1-wei donation flips the branch. Fix: add explicit mode arguments to lock() (lockToken0Only / lockToken1Only / lockBalanced) that ignore the other side, or treat a balance below a minimum (e.g. 1e6 wei) as zero for branch selection and keep it in the contract.","line":76,"path":"src/DeezLiquidityLock.sol","reproduction":"DeezBase fixture (token is currency0), seedTokenOnly(1e24). Transfer 10_000_000e18 $DEEZ to hook.liquidityLock(); attacker mints/transfers 1 wei $IMD to the same address; call lock(). Expected (per the contract's own description): a $DEEZ-only position just above the price holding ~10M $DEEZ. Actual: lock() returns liquidity 1, token.balanceOf(lock) = 9999999999999999999999999 (10M minus 1 wei), imd.balanceOf(lock) = 0, totalLocked0 = 1, totalLocked1 = 1. See test/scratch/LockDust.t.sol test_oneWeiOfImdFlipsTheLockIntoAFullRangeDustPosition (logs these values).","severity":"low","snippet":"        if (balance1 == 0) {\n            // Token0 only: the whole position must sit above the current price.\n            tickLower = _ceilToSpacing(tick + 1, spacing);\n            if (tickLower >= tickUpper) tickLower = tickUpper - spacing;\n        } else if (balance0 == 0) {\n            // Token1 only: the whole position must sit at or below the current price.\n            tickUpper = _floorToSpacing(tick, spacing);\n            if (tickUpper <= tickLower) tickUpper = tickLower + spacing;\n        }","title":"lock() picks its mode from strict zero-balance checks: one wei of the other token sent ahead of a one-sided lock turns the whole balance into a liquidity-1 full-range position and leaves the main bala"}],"hash":"3d4e754176f8428b9fe9885781a7b33a8da518a0ba3d31240684b76b06fc9ae5","nodeId":"8c4a73bf-d036-4ba6-9642-a8b5b4fcd398","outcome":"completed","summary":"Review complete. The findings file at `.imd-findings.json` holds two substantiated findings and a coverage row for every listed entry point.\n\n**Findings**\n\n- **Medium, `src/DeezHook.sol:204`.** When $IMD is the specified currency, the hook derives the fee from the requested amount and never checks how much the pool actually filled. On the launch pool, which holds $DEEZ only, an exact-output sell of 1000 $IMD swaps nothing yet the trader pays 20.41 $IMD for it. A partially filled exact-input buy is charged 2% of the request, which was 7.3% of what the trader actually paid in my run. Standard routers turn the unfilled case into a revert, but any direct integrator pays. The proof test under `test/scratch/` fails on both cases now and passes with either a revert-on-partial-fill fix or a fee on the filled amount.\n- **Low, `src/DeezLiquidityLock.sol:76`.** The lock picks its mode from strict zero-balance checks. One wei of $IMD sent ahead of a $DEEZ-only lock produces a full-range position of liquidity 1 and leaves the 10M $DEEZ in the contract. Funds are not lost and a private relay avoids it, so it is griefing only.\n\n**What held**\n\n- All four fee formulas net to zero against the minted claim and round down by at most 1 wei.\n- Reward shares floor per holder, sum to at most the pot, use the same formula in claimable and claim, and the snapshot second is always final when hasEnded is true.\n- The uint128 cast in accrue is unreachable. IMD's mainnet supply is about 1.1e27 wei, far below 2^128.\n- The lock's 1 ppm liquidity shave covered the manager's round-up for every fuzzed amount from 1 wei to 5e6 wei.\n- Existing suite: 80 tests pass. Slither and Aderyn leads were checked and the remaining ones are benign or unreachable.\n\n**Not covered in depth:** the end-of-epoch snapshot can be timed by buying just before and selling just after the day boundary. I judged it unprofitable against the 6.5% round-trip fees and left it for the economic reviewer.","treeHash":null,"usage":{"cachedInputTokens":1365929,"inputTokens":386,"model":"claude-fable-5-1","outputTokens":45889,"runtime":"claude","turns":30,"wallClockMs":740644}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"505a745a77612e11","findings":[{"citation":"resolved","description":"When $IMD is the specified currency (exact-input buy: amountSpecified = -in; exact-output sell: amountSpecified = +out) beforeSwap returns the whole fee (in*2% or out*2/98) as a positive specified BeforeSwapDelta, and afterSwap (line 167) mints the same _specifiedFee(params) as an ERC-6909 claim without ever looking at the realised specified-side delta. v4 credits the hook delta and debits the trader whatever the pool then fills (Hooks.afterSwap: swapDelta = swapDelta - hookDelta), and Pool.swap fills less than requested without reverting whenever the trader's sqrtPriceLimitX96 is reached or liquidity on that side runs out. On the launch pool, which is seeded with $DEEZ only and holds just the $IMD that buys brought in, an exact-output sell for more $IMD than the pool holds is the ordinary case, and every router passes a price limit for slippage protection. Result: an exact-input buy stopped by its limit pays 2% of the full request for a sliver of output (a 99.7% effective fee in the reproduction), and an exact-output sell that fills less than 2.04% of the request has a negative $IMD delta, so the seller pays $DEEZ and $IMD and receives nothing. The surplus is minted to the holder pot, i.e. paid to the wrong party, and the brief's 'exactly 2% on every buy and every sell' is broken. The unspecified-side path (_unspecifiedFee, line 213) reads the real delta and is correct. Merged from audit_permissions, audit_flow, audit_math and write_foundry_tests, which all report this root cause. Minimal fix: in afterSwap, when $IMD is the specified side, compare the realised specified delta with what beforeSwap assumed (exact input: |delta.specified| == in - fee; exact output: delta.specified == out + fee) and revert on a partial fill; or move the whole fee to afterSwap and compute it from the realised delta.","line":154,"path":"src/DeezHook.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 {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.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 {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 {TickMath} from \"v4-core/src/libraries/TickMath.sol\";\nimport {DeezToken} from \"src/DeezToken.sol\";\nimport {DeezHook} from \"src/DeezHook.sol\";\n\ncontract ScratchIMD is ERC20 {\n    constructor() ERC20(\"IMD\", \"IMD\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\n/// @dev Minimal router paying from its own balance; lets the caller pick the price limit.\ncontract ScratchRouter is IUnlockCallback {\n    IPoolManager public immutable manager;\n\n    constructor(IPoolManager m) {\n        manager = m;\n    }\n\n    function swap(PoolKey memory key, bool zeroForOne, int256 amountSpecified, uint160 limit)\n        external\n        returns (BalanceDelta)\n    {\n        return abi.decode(manager.unlock(abi.encode(uint8(1), key, SwapParams(zeroForOne, amountSpecified, limit))), (BalanceDelta));\n    }\n\n    function addLiquidity(PoolKey memory key, int24 lower, int24 upper, int256 liquidity) external {\n        manager.unlock(abi.encode(uint8(2), key, lower, upper, liquidity));\n    }\n\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        require(msg.sender == address(manager));\n        uint8 kind = abi.decode(data, (uint8));\n        PoolKey memory key;\n        BalanceDelta delta;\n        if (kind == 1) {\n            SwapParams memory p;\n            (, key, p) = abi.decode(data, (uint8, PoolKey, SwapParams));\n            delta = manager.swap(key, p, \"\");\n        } else {\n            int24 lower;\n            int24 upper;\n            int256 liq;\n            (, key, lower, upper, liq) = abi.decode(data, (uint8, PoolKey, int24, int24, int256));\n            (delta,) = manager.modifyLiquidity(key, ModifyLiquidityParams(lower, upper, liq, bytes32(0)), \"\");\n        }\n        _settle(key.currency0, delta.amount0());\n        _settle(key.currency1, delta.amount1());\n        return abi.encode(delta);\n    }\n\n    function _settle(Currency c, int128 amount) private {\n        if (amount < 0) {\n            manager.sync(c);\n            IERC20(Currency.unwrap(c)).transfer(address(manager), uint256(uint128(-amount)));\n            manager.settle();\n        } else if (amount > 0) {\n            manager.take(c, address(this), uint256(uint128(amount)));\n        }\n    }\n}\n\ncontract FeeOnRequestedAmountTest is Test {\n    uint160 constant FLAGS = (1 << 13) | (1 << 7) | (1 << 6) | (1 << 3) | (1 << 2);\n    uint160 constant SQRT_PRICE_1_1 = 79228162514264337593543950336;\n    int24 constant SPACING = 60;\n\n    PoolManager manager;\n    DeezToken token;\n    ScratchIMD imd;\n    DeezHook hook;\n    ScratchRouter router;\n    PoolKey key;\n    bool tokenIsCurrency0;\n\n    function setUp() public {\n        manager = new PoolManager(address(this));\n        token = new DeezToken();\n        imd = new ScratchIMD();\n        tokenIsCurrency0 = address(token) < address(imd);\n\n        bytes memory creation = abi.encodePacked(\n            type(DeezHook).creationCode, abi.encode(IPoolManager(address(manager)), address(token), address(imd))\n        );\n        bytes32 h = keccak256(creation);\n        for (uint256 i = 0; i < 500_000; i++) {\n            address p = address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), bytes32(i), h)))));\n            if ((uint160(p) & ((1 << 14) - 1)) == FLAGS && p.code.length == 0) {\n                hook = new DeezHook{salt: bytes32(i)}(IPoolManager(address(manager)), address(token), address(imd));\n                break;\n            }\n        }\n        require(address(hook) != address(0), \"no salt\");\n\n        (Currency c0, Currency c1) = tokenIsCurrency0\n            ? (Currency.wrap(address(token)), Currency.wrap(address(imd)))\n            : (Currency.wrap(address(imd)), Currency.wrap(address(token)));\n        key = PoolKey(c0, c1, 12_500, SPACING, IHooks(address(hook)));\n        manager.initialize(key, SQRT_PRICE_1_1);\n\n        router = new ScratchRouter(manager);\n        // The launch pool holds $DEEZ only, in the range just above the opening price.\n        token.transfer(address(router), 100_000_000 ether);\n        (int24 lower, int24 upper) = tokenIsCurrency0\n            ? (SPACING, TickMath.maxUsableTick(SPACING))\n            : (TickMath.minUsableTick(SPACING), -SPACING);\n        router.addLiquidity(key, lower, upper, 1e24);\n        imd.mint(address(router), 1_000_000 ether);\n    }\n\n    /// Exact-output sell of 1000 $IMD on a pool with no $IMD liquidity: the pool fills nothing, but the hook still\n    /// charges 2/98 of the requested 1000 $IMD. The trader pays ~20.41 $IMD and receives nothing.\n    function test_exactOutputSellThatFillsNothingStillChargesTheFee() public {\n        uint256 imdBefore = imd.balanceOf(address(router));\n        uint256 deezBefore = token.balanceOf(address(router));\n\n        // sell = token in, IMD out: zeroForOne when token is currency0. Full price limit, as routers set it.\n        bool zeroForOne = tokenIsCurrency0;\n        uint160 limit = zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1;\n        // A fix that refuses partial fills is acceptable: a revert here is a pass.\n        try router.swap(key, zeroForOne, int256(1_000 ether), limit) {} catch { return; }\n\n        uint256 imdPaid = imdBefore - imd.balanceOf(address(router));\n        uint256 deezPaid = deezBefore - token.balanceOf(address(router));\n        emit log_named_uint(\"DEEZ paid\", deezPaid);\n        emit log_named_uint(\"IMD paid (should be 0: nothing was received)\", imdPaid);\n        emit log_named_uint(\"hook pendingFees\", hook.pendingFees());\n\n        assertEq(deezPaid, 0, \"pool swapped nothing\");\n        // A fee on a swap that moved nothing is wrong: the trader must not pay IMD on a sell that returned none.\n        assertEq(imdPaid, 0, \"trader charged a fee although no IMD was received\");\n        assertEq(hook.pendingFees(), 0);\n    }\n\n    /// Exact-input buy of 1000 $IMD with a price limit just above the opening price: the pool takes only part of\n    /// the input, but the hook charges 2% of the full requested 1000 $IMD.\n    function test_exactInputBuyPartialFillIsChargedOnTheRequestedInput() public {\n        uint256 imdBefore = imd.balanceOf(address(router));\n        bool zeroForOne = !tokenIsCurrency0; // IMD in, token out\n        // Limit: the price right after the first liquidity tick, so the swap fills only a sliver.\n        int24 limitTick = tokenIsCurrency0 ? int24(SPACING + 5) : int24(-SPACING - 5);\n        uint160 limit = TickMath.getSqrtPriceAtTick(limitTick);\n        // A fix that refuses partial fills is acceptable: a revert here is a pass.\n        try router.swap(key, zeroForOne, -int256(1_000 ether), limit) {} catch { return; }\n        uint256 imdPaid = imdBefore - imd.balanceOf(address(router));\n        uint256 fee = hook.pendingFees();\n        emit log_named_uint(\"IMD paid total\", imdPaid);\n        emit log_named_uint(\"IMD to the pool\", imdPaid - fee);\n        emit log_named_uint(\"hook fee\", fee);\n        // The fee must be 2% of what the trader actually paid in total (1 wei of rounding allowed).\n        assertApproxEqAbs(fee, imdPaid * 200 / 10_000, 1, \"fee is not 2% of the gross IMD actually paid\");\n    }\n}","reproduction":"Fresh PoolManager, DeezToken, a mock $IMD, DeezHook mined onto flags 0x20CC, pool initialised at sqrtPrice 2^96 with fee 12500, spacing 60 and 1e24 liquidity of $DEEZ only in [60, maxUsableTick] as the factory seeds it (test/scratch/Proof_7251c9554384.t.sol). (a) Router swaps zeroForOne = tokenIsCurrency0, amountSpecified = +1000e18 (exact-output sell of 1000 $IMD), limit MIN_SQRT_PRICE+1. Expected: revert or no fee, since the pool has no $IMD to pay out. Actual: DEEZ paid = 0, the router's $IMD balance falls by 20408163265306122448 (= 1000e18 * 200 / 9800) and hook.pendingFees() = 20408163265306122448. (b) Router swaps exact-input buy amountSpecified = -1000e18 with sqrtPriceLimit = sqrtPriceAtTick(65): the pool consumes 253.94e18 $IMD, the trader pays 273.94e18 in total, the hook keeps 20e18 (7.3% of the gross; expected 5.48e18 = 2%). Both tests fail on this code with exactly these numbers; write_foundry_tests' Proof_f14bbe5777d1 (balanced pool, limit 5 ticks away) shows the same: 200 IMD fee on 225.3 IMD paid, and a seller whose IMD balance falls by 179.09 IMD.","severity":"high","snippet":"        uint256 fee = _specifiedFee(params);","title":"Hook fee on $IMD-specified swaps is fixed from the requested amount in beforeSwap, so a partially filled swap pays far more than 2% and an exact-output seller can end up paying $IMD"},{"citation":"resolved","description":"isExcluded (line 107) excludes only the PoolManager, 0x..dEaD, the hook and DeezRewards itself. The factory's split, as the README states (lines 118-119), puts 88% of supply into the PoolManager (excluded), 10% into the swarm's Merkle distributor and 2% with the requester, so at launch eligibleSupplyAt(e) = 1,000,000,000 - 880,000,000 = 120,000,000 DEEZ and the distributor contract's weight is 100,000,000 / 120,000,000 = 83.3% of every pot until swarm members withdraw. The README (lines 183-185) accepts this on the ground that the distributor's share is 'unclaimable and is recycled to holders after the claim window'. It is not: claim(account, epochs) has no caller check and transfers to `account` (line 185), so any address can call claim(distributor, [e]) once epoch e ended, pot.claimed then counts the amount (line 179) and recycle(e) (line 194) folds back nothing. Nothing in this tree gives the distributor, or any other DEEZ-holding contract (other DEX pools, lockers, vaults, CEX contracts), a way to move $IMD, so the $IMD is frozen for good. Up to 5/6 of every early pot can be destroyed by anyone for the cost of gas, instead of reaching holders as the brief requires ('All of this 2% ... must be accumulated and redistributed to all eligible holders'). Even without the push, 83% of early fees sit idle for 366 epochs. Merged from audit_economics (high), audit_flow (high) and write_foundry_tests (medium). Fix: require msg.sender == account in claim() (or let the account name a recipient) so no third party can direct a share into a contract that did not ask for it, and additionally exclude the swarm distributor from eligibility (pass \"$factory\" and \"$launchNumber\" to the hook and on to DeezRewards and read IFactory(factory).distributorOf(launchNumber) lazily at finalize/claim time).","line":166,"path":"src/DeezRewards.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 {DeezToken} from \"src/DeezToken.sol\";\nimport {DeezRewards} from \"src/DeezRewards.sol\";\n\ncontract Imd is ERC20 {\n    constructor() ERC20(\"IMD\", \"IMD\") {}\n    function mint(address to, uint256 a) external { _mint(to, a); }\n}\n\n/// @dev Stands in for the swarm's Merkle distributor: holds DEEZ, has no way to move any other token.\ncontract Distributor {}\n\ncontract ClaimForContractTest is Test {\n    function test_thirdPartyCannotStrandShareOfContractHolder() public {\n        vm.warp(10 days);\n        DeezToken token = new DeezToken();\n        Imd imd = new Imd();\n        DeezRewards rewards = new DeezRewards(address(token), address(imd), address(0xBEEF1), address(0xBEEF2));\n        Distributor dist = new Distributor();\n        address holder = address(0xA11CE);\n        token.transfer(address(dist), 100_000_000 ether); // swarm 10%\n        token.transfer(holder, 900_000_000 ether);\n\n        imd.mint(address(rewards), 1000 ether);\n        rewards.accrue();\n        uint256 epoch = rewards.currentEpoch();\n        vm.warp(block.timestamp + 2 days);\n\n        // Any third party pays the distributor's share to a contract that can never move it.\n        uint256[] memory epochs = new uint256[](1);\n        epochs[0] = epoch;\n        vm.prank(address(0xBAD));\n        try rewards.claim(address(dist), epochs) {} catch {}\n\n        // The holder ends up with the full 10% that a contract which can't spend it would otherwise freeze.\n        assertEq(imd.balanceOf(address(dist)), 0, \"IMD stranded in non-claiming contract\");\n    }\n}","reproduction":"test/scratch/Proof_26cc3b1283ec.t.sol: DeezToken, mock IMD, DeezRewards(token, imd, 0xBEEF1, 0xBEEF2); a contract with no functions holds 100,000,000 DEEZ, holder 0xA11CE holds 900,000,000; mint 1000 IMD to rewards and accrue() in epoch e; warp 2 days; vm.prank(0xBAD); rewards.claim(distributor, [e]). Expected: a stranger cannot direct the distributor's share, so imd.balanceOf(distributor) stays 0 and recycle(e) after 366 epochs returns the 100 IMD to holders. Actual: the call succeeds, imd.balanceOf(distributor) = 100e18 (10% of the pot), the IMD is unreachable forever and recycle(e) later recycles 0 of it. The proof fails on this code with 'IMD stranded in non-claiming contract: 100000000000000000000 != 0'. With the real launch split and no other holder, the same call moves 83.3% of the pot.","severity":"high","snippet":"    function claim(address account, uint256[] calldata epochs) external nonReentrant returns (uint256 total) {","title":"claim() is callable by anyone for any non-excluded account and always pays that account, so a third party can push a contract holder's share (the swarm Merkle distributor with 10% of supply, ~83% of t"},{"citation":"resolved","description":"accrue() credits everything received since the last call to currentEpoch(), recycle() (line 196-197) drops a closed epoch's remainder into the current epoch, and claimable()/claim() (lines 128, 177) weight by token.getPastBalanceOf(account, snapshotOf(e)), the last second of the day. Fees are pooled as pendingFees in the hook until anyone calls harvest(), so nothing ties a reward to the time a balance was held or to the day the fees were traded: the actor who calls harvest() chooses which day's snapshot the money is weighted by, and a balance held for one block across the boundary weighs the same as one held all day. Round-trip cost is about 6.5% (2% hook + 1.25% LP each way) plus price impact, so the snipe is profitable whenever the pot share exceeds that, which at launch-scale prices is almost always (see reproduction). Long-term holders are diluted, against the brief's intent that the 2% goes to holders weighted by their holding. Merged from audit_economics (medium), audit_flow (low) and write_foundry_tests (low); the project's own test test_balanceAtTheSnapshotSecondCountsAndTheNextSecondDoesNot documents the semantics. Fix: credit fees to the epoch they were traded in (per-epoch pendingFees in afterSwap, harvest() moving each epoch's amount into its own pot, recycle() into a fixed later epoch) so harvest timing cannot be chosen, and/or weight by time-averaged balance over the epoch (the Checkpoints history already allows integrating).","line":147,"path":"src/DeezRewards.sol","reproduction":"State: 5,000 IMD of un-harvested pendingFees on the hook, eligible supply 120,000,000 DEEZ, no other holder has harvested. At block.timestamp = (e+1)*86400 - 1 the attacker buys 30,000,000 DEEZ, then calls hook.harvest(): accrue() runs with currentEpoch() == e, so pots[e].total += 5,000 IMD (+ the attacker's own buy fee). At (e+1)*86400 (hasEnded(e) is now true) the attacker sells back and calls rewards.claim(attacker, [e]): payout = 5,000 * 30,000,000 / 150,000,000 = 1,000 IMD for one block of holding; every holder who held through the trades that earned those fees gets 20% less. At the policy's opening cap (2,500 IMD for 1B DEEZ, 2.5e-6 IMD per DEEZ) the 30M DEEZ position is worth about 75 IMD, so the 6.5% round-trip fee (~5 IMD) is negligible against the 1,000 IMD taken; the snipe stays profitable up to a DEEZ price of about 5e-4 IMD for a 5,000 IMD pot. Expected: a share proportional to balance over the period the fees were earned, or fees credited to the epoch of the trade; actual: pot of the harvest epoch, weighted by the end-second balance.","severity":"medium","snippet":"        uint256 epoch = currentEpoch();\n        pots[epoch].total += uint128(amount);","title":"Rewards are credited to the epoch in which the permissionless harvest()/recycle() happens and weighted by a one-second balance snapshot, so a wallet that holds only across the UTC day boundary and tim"},{"citation":"resolved","description":"lock() is permissionless and reads both balances and slot0 at call time; it has no mode argument and no tick/price bound. The README tells the requester to transfer >= 10,000,000 DEEZ to hook.liquidityLock() and call lock() in two steps. (1) Anyone can front-run the call with 1 wei of $IMD: balance1 != 0, so the full-range branch runs, _liquidityFor returns min(l0, l1) = 1, the position consumes 1 wei of each token and 9,999,999.999999999999999999 DEEZ stay in the contract; the griefer can repeat for 1 wei + gas per attempt, so the brief's 'lock it permanently' needs a private bundle or an atomic transfer-and-lock helper. (2) If the requester sends both tokens at any ratio other than the pool's, only min(l0, l1) is added and the larger side is stranded until the dust on the limiting side converges to zero over several calls (8 calls observed). (3) A seller can depress the price in the same block, call lock(), and have the DEEZ placed as sell-side liquidity from the depressed tick upward, then buy it back; with 10M DEEZ against the pool's 880M the extractable value is small against the 6.5% round-trip fees, so this is a missing bound, not a demonstrated profit. Nothing is lost (every token can only ever end in the pool), so this is a liveness/griefing issue. Slither's incorrect-equality hits on lines 70, 76 and 80 are this defect. Merged from audit_flow (medium), audit_math (low) and write_foundry_tests (medium). Fix: let the caller choose the mode explicitly (lockToken0Only / lockToken1Only / lockBalanced, ignoring the other side, or locking the leftover of the larger side single-sided in the same unlock) and accept a caller-supplied tick bound.","line":76,"path":"src/DeezLiquidityLock.sol","reproduction":"test/scratch/LockGrief.t.sol on the DeezBase fixture (DEEZ is currency0, balanced 1e21 liquidity at 1:1): token.transfer(lock, 10_000_000 ether); imd.mint(lock, 1); lock.lock(). Expected: a DEEZ-only position just above the price holding ~10M DEEZ. Actual: lock() returns liquidity 1, totalLocked0 = 1, totalLocked1 = 1, token.balanceOf(lock) = 9999999999999999999999999 (a second call, with no further griefing, then locks 10,029,030 liquidity single-sided). Second case: 10_000_000 ether DEEZ + 1_000 ether IMD: the first lock() adds liquidity 999999000000000000054 and leaves 9,999,000 DEEZ and 0.001 IMD; the IMD dust reaches zero only on the fourth call, and the DEEZ is finally locked single-sided on the fifth. Third case: after sell(-5_000 ether) moves the tick from 0 to -35628, lock() places the 10M DEEZ position starting at the depressed tick.","severity":"low","snippet":"        if (balance1 == 0) {","title":"lock() decides position mode and range from state anyone can move (the lock's balances and the spot tick): 1 wei of the other token turns a single-sided 10,000,000 DEEZ lock into a liquidity-1 full-ra"},{"citation":"resolved","description":"The brief requires that at least 10,000,000 DEEZ of the requester's 2% be locked into liquidity. Under the factory model the 2% goes to the paying wallet and no contract in this tree receives supply at launch (README lines 118-119), so the lock is an operational step (README step 1) that nothing on-chain compels or verifies. This is a limitation of the deployment model rather than a code defect, recorded so the admission check knows the constraint is not enforced by the accepted code.","line":65,"path":"src/DeezLiquidityLock.sol","reproduction":"After the launch transaction the requester wallet holds 20,000,000 DEEZ and hook.liquidityLock() holds 0; lockedLiquidity stays empty until someone transfers DEEZ there and calls lock(). Expected per brief: >= 10,000,000 DEEZ permanently locked; actual: only if the requester does it.","severity":"info","snippet":"    function lock() external returns (uint128 liquidity) {","title":"The brief's mandatory 1% liquidity lock is not enforced by any contract: the factory sends the full 2% to the requester and the lock depends on a voluntary post-launch transfer"}],"hash":"47761209efb5ff4bf26ca3075967d6903cdf707f4982b378d7587934727e3c3c","nodeId":"c32d4281-49db-43c5-a91e-6b5f5c54b6c4","outcome":"completed","summary":"The review is complete and `.imd-findings.json` holds the result. No source file was changed; only the findings file and scratch tests under `test/scratch/` were written.\n\n**Outcome: 5 findings kept out of 13 specialist reports, after merging duplicates and re-running every proof.** All four attached proofs fail on this code for the reasons their findings state. The project's own suite passes (109 tests), contract sizes are well within the deploy limits, and the manifest matches the constructor and permissions.\n\nFindings, by severity:\n\n1. **High, partial-fill fee overcharge** in `src/DeezHook.sol:154`. The fee on $IMD-specified swaps is fixed in beforeSwap from the requested amount and minted again in afterSwap, never checked against what the pool filled. On the DEEZ-only launch pool an exact-output sell of 1,000 IMD fills nothing yet charges the seller 20.4 IMD, and a limit-stopped exact-input buy pays a 7.3% effective fee. Four specialists reported this one root cause. Proof attached, from the math specialist, which accepts either a revert-on-partial-fill fix or a realised-delta fee.\n\n2. **High, permissionless claim strands $IMD** in `src/DeezRewards.sol:166`. Anyone can claim for any contract holder and the payout always goes to that contract. The swarm Merkle distributor holds 10% of supply, which is 83% of the eligible float at launch, and has no way to move $IMD, so a stranger can destroy most of every early pot and recycle() returns nothing. Three specialists reported it. Proof attached.\n\n3. **Medium, snapshot and harvest timing** in `src/DeezRewards.sol:147`. Fees are credited to whichever epoch is current when harvest() or recycle() is called, and weighted by a one-second balance. A buyer at the day boundary who harvests in that block takes the accumulated pot for one block of holding. At launch-scale prices the 6.5% round-trip cost is negligible against the payout.\n\n4. **Low, lock() mode and range follow movable state** in `src/DeezLiquidityLock.sol:76`. Reproduced in my scratch test: 1 wei of $IMD turns a 10M DEEZ single-sided lock into a liquidity-1 position, unbalanced deposits need five calls, and the range follows a depressed tick. No funds are lost, so I lowered the two medium ratings.\n\n5. **Info, the 1% lock is not enforced on-chain**. It relies on the requester acting after launch.\n\nCoverage answers all 13 entry points plus two invariant rows. The remaining entry points hold: beforeInitialize, harvest, both unlockCallbacks, finalize, and the three token functions were traced through their guards and state changes.","treeHash":null,"usage":{"cachedInputTokens":1258886,"inputTokens":354,"model":"claude-fable-5-1","outputTokens":30879,"runtime":"claude","turns":28,"wallClockMs":481394}},{"artifacts":[],"attempt":1,"bundleHash":"d06e9c7b8c1e899f8a784d07a38ceda14af416fd7512b5c8fc5c292414d20ca5","device":"f494611affb5524c","findings":[{"description":"For exact-input buys and exact-output sells ($IMD specified) the fee is computed in beforeSwap from params.amountSpecified and returned as a BeforeSwapDelta, then minted in afterSwap from the same params. When the swap stops early at the caller's sqrtPriceLimitX96 (any router or aggregator that passes a price limit), the pool trades only part of the request but the hook still keeps 2% (or 2/98) of the full request. The trader pays that fee on volume that never traded: an exact-input buy can pay many times 2%, and on an exact-output sell the hook's fee exceeds what the pool paid out, so the seller of $DEEZ ends up owing $IMD on top. This breaks the brief's 'exactly 2% on every buy and every sell'. The fix is to compute the fee from the executed BalanceDelta in afterSwap (or to cap it at 2% of the IMD actually swapped and return the excess).","line":201,"path":"src/DeezHook.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 {IUnlockCallback} from \"v4-core/src/interfaces/callback/IUnlockCallback.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 {TickMath} from \"v4-core/src/libraries/TickMath.sol\";\nimport {DeezToken} from \"src/DeezToken.sol\";\nimport {DeezHook} from \"src/DeezHook.sol\";\n\ncontract PairToken is ERC20 {\n    constructor() ERC20(\"IMD\", \"IMD\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\n/// @dev Router that passes the caller's price limit through, as production routers do.\ncontract LimitRouter is IUnlockCallback {\n    IPoolManager immutable manager;\n\n    constructor(IPoolManager manager_) {\n        manager = manager_;\n    }\n\n    function swap(PoolKey memory key, SwapParams memory params) external returns (BalanceDelta) {\n        return abi.decode(manager.unlock(abi.encode(true, key, params, int24(0), int24(0))), (BalanceDelta));\n    }\n\n    function addLiquidity(PoolKey memory key, int24 lower, int24 upper, int256 liquidity) external {\n        manager.unlock(abi.encode(false, key, SwapParams(false, liquidity, 0), lower, upper));\n    }\n\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        (bool isSwap, PoolKey memory key, SwapParams memory params, int24 lower, int24 upper) =\n            abi.decode(data, (bool, PoolKey, SwapParams, int24, int24));\n        BalanceDelta delta;\n        if (isSwap) {\n            delta = manager.swap(key, params, \"\");\n        } else {\n            (delta,) = manager.modifyLiquidity(key, ModifyLiquidityParams(lower, upper, params.amountSpecified, 0), \"\");\n        }\n        _settle(key.currency0, delta.amount0());\n        _settle(key.currency1, delta.amount1());\n        return abi.encode(delta);\n    }\n\n    function _settle(Currency currency, int128 amount) private {\n        if (amount < 0) {\n            manager.sync(currency);\n            ERC20(Currency.unwrap(currency)).transfer(address(manager), uint256(uint128(-amount)));\n            manager.settle();\n        } else if (amount > 0) {\n            manager.take(currency, address(this), uint256(uint128(amount)));\n        }\n    }\n}\n\n/// @notice The hook must take 2% of the $IMD that actually changes hands. When a swap stops at the trader's\n/// price limit, a fee computed in beforeSwap from the requested amount is charged on volume that never traded.\ncontract PartialFillFeeTest is Test {\n    uint160 constant FLAGS = (1 << 13) | (1 << 7) | (1 << 6) | (1 << 3) | (1 << 2);\n    PoolManager manager;\n    DeezToken token;\n    PairToken imd;\n    DeezHook hook;\n    LimitRouter router;\n    PoolKey key;\n    bool tokenIs0;\n\n    function setUp() public {\n        manager = new PoolManager(address(this));\n        token = new DeezToken();\n        imd = new PairToken();\n        tokenIs0 = address(token) < address(imd);\n        bytes memory code =\n            abi.encodePacked(type(DeezHook).creationCode, abi.encode(address(manager), address(token), address(imd)));\n        bytes32 h = keccak256(code);\n        for (uint256 i = 0;; i++) {\n            address p =\n                address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), bytes32(i), h)))));\n            if (uint160(p) & ((1 << 14) - 1) != FLAGS) continue;\n            hook = new DeezHook{salt: bytes32(i)}(IPoolManager(address(manager)), address(token), address(imd));\n            break;\n        }\n        (address c0, address c1) = tokenIs0 ? (address(token), address(imd)) : (address(imd), address(token));\n        key = PoolKey(Currency.wrap(c0), Currency.wrap(c1), 12_500, 60, IHooks(address(hook)));\n        manager.initialize(key, TickMath.getSqrtPriceAtTick(0));\n        router = new LimitRouter(manager);\n        token.transfer(address(router), 1_000_000 ether);\n        imd.mint(address(router), 1_000_000 ether);\n        router.addLiquidity(key, -600, 600, 100_000 ether);\n    }\n\n    function test_exactInputBuyStoppedByPriceLimitIsChargedTwoPercentOfWhatTraded() public {\n        bool zeroForOne = !tokenIs0; // buy: $IMD in\n        int24 limitTick = zeroForOne ? int24(-5) : int24(5);\n        uint256 requested = 10_000 ether;\n        uint256 imdBefore = imd.balanceOf(address(router));\n        router.swap(key, SwapParams(zeroForOne, -int256(requested), TickMath.getSqrtPriceAtTick(limitTick)));\n        uint256 paid = imdBefore - imd.balanceOf(address(router));\n        uint256 fee = hook.pendingFees();\n        assertLt(paid, requested, \"the swap stopped at the limit\");\n        // Expected: fee == 2% of the $IMD the trader actually paid. Actual: 2% of the requested input (200 IMD\n        // of the ~225 IMD paid).\n        assertApproxEqAbs(fee, paid * 200 / 10_000, 1, \"fee is 2% of the IMD that changed hands\");\n    }\n\n    function test_exactOutputSellStoppedByPriceLimitIsChargedTwoPercentOfWhatTraded() public {\n        bool zeroForOne = tokenIs0; // sell: $DEEZ in, $IMD out\n        int24 limitTick = zeroForOne ? int24(-5) : int24(5);\n        uint256 requested = 10_000 ether;\n        uint256 imdBefore = imd.balanceOf(address(router));\n        router.swap(key, SwapParams(zeroForOne, int256(requested), TickMath.getSqrtPriceAtTick(limitTick)));\n        int256 received = int256(imd.balanceOf(address(router))) - int256(imdBefore);\n        uint256 fee = hook.pendingFees();\n        // Expected: a seller never pays $IMD, and the fee is 2% of the gross $IMD the pool paid out.\n        // Actual: the fee is 2/98 of the requested output, more than the pool paid out, so the seller pays $IMD too.\n        assertGe(received, 0, \"a sell never costs the seller IMD\");\n        assertLt(uint256(received), requested, \"the swap stopped at the limit\");\n        assertApproxEqAbs(fee, (uint256(received) + fee) * 200 / 10_000, 1, \"fee is 2% of the IMD that changed hands\");\n    }\n}","reproduction":"Pool at tick 0 with liquidity 100,000e18 in [-600, 600]. (1) Exact-input buy of 10,000 IMD with a price limit 5 ticks away: the trader pays ~225.3 IMD, of which hook.pendingFees() = 200 IMD (expected ~4.5 IMD = 2% of 225.3). (2) Exact-output sell asking 10,000 IMD with a limit 5 ticks away: the hook mints 204.08 IMD in fees while the pool paid out only ~25 IMD, so the seller's IMD balance falls by 179.09 IMD although they sold DEEZ (expected: IMD balance rises, fee = 2% of the gross paid out). Run test/scratch/PartialFillFee.t.sol (proof below): both tests fail on the current code.","severity":"high","title":"Hook fee on the specified side is charged on the requested amount, not the traded amount, so swaps stopped by their price limit overpay (sellers can even pay IMD)"},{"description":"claim(account, epochs) is callable by anyone for any non-excluded account and always transfers to that account. The README says the swarm's Merkle distributor (up to 10% of supply, not excluded) has an 'unclaimable' share that is recycled to holders after CLAIM_WINDOW. That is false: any third party can call claim(merkleDistributor, epochs) and push its share into a contract that has no way to move IMD, stranding up to ~10% of every epoch's rewards permanently instead of letting recycle() return it to holders. The same applies to any other contract holding DEEZ (other DEX pools, lockers, vaults, the liquidity lock's leftovers are fine since lock() reuses them). Cannot be tested around: the behaviour itself is what the brief allows ('anyone should be able to trigger distribution'), so the fix is a design choice (exclude the known distributor at deploy, or let only EOAs/the account itself claim for contracts).","line":166,"path":"src/DeezRewards.sol","reproduction":"DeezRewards with 100e18 IMD in epoch e; contract X (no IMD transfer function) holds 10% of eligible supply at snapshotOf(e). After e ends a stranger calls claim(X, [e]): it succeeds and transfers 10e18 IMD to X. After CLAIM_WINDOW, recycle(e) returns only 0 for X's share. Expected per README: X's share is recycled to holders; actual: it is stuck in X forever.","severity":"medium","title":"Anyone can force-claim rewards into holder contracts that cannot use IMD, defeating the recycling the README relies on"},{"description":"lock() places single-sided DEEZ just above the current tick (or IMD just below it) using slot0 at call time, with no minimum price, no expected tick and no caller restriction. The README tells the requester to transfer >=10,000,000 DEEZ to the lock and then call lock() 'from any wallet', i.e. in two transactions. Between them anyone can push the price down (sell DEEZ), call lock() so the requester's DEEZ is placed as liquidity starting at the depressed price, then buy that DEEZ back cheaply. The locked position ends up converted to far less IMD than the DEEZ was worth, weakening the pool instead of strengthening it. Not asserted in tests: whether it is profitable depends on pool depth versus the 2% + 1.25% fees on both legs; the missing bound is the defect.","line":72,"path":"src/DeezLiquidityLock.sol","reproduction":"Send 10,000,000 DEEZ to hook.liquidityLock(). Attacker sells DEEZ to move the tick down by N spacings, calls lock() (position = [tick+1 rounded, maxTick]), then buys DEEZ back through the new position. Expected: lock() refuses or locks at a caller-bounded price; actual: it locks at whatever price the attacker set.","severity":"medium","title":"DeezLiquidityLock.lock() is permissionless and reads the spot price with no bound, so the README's two-step 'transfer then lock' can be sandwiched"},{"description":"claimable() uses the balance at snapshotOf(e), the last second of each UTC day, which is public and known in advance, and pots[e] is visible as fees are harvested during the day. A wallet that buys in the snapshot second and sells in the next one is paid exactly like a holder of the whole day. The round trip costs ~6.5% (2% hook + 1.25% LP each way), so it pays only when one day's pot exceeds ~6.5% of the eligible market value, but it is not 'weighted by balance' over time as holders may expect. A time-weighted balance would remove it. Reported, not asserted: tests document the snapshot-second semantics (test_balanceAtTheSnapshotSecondCountsAndTheNextSecondDoesNot) without blessing it as intended economics.","line":97,"path":"src/DeezRewards.sol","reproduction":"Epoch e pot = P, eligible supply S. Wallet W holds 0 DEEZ until snapshotOf(e), buys B DEEZ in that second, sells B in snapshotOf(e)+1. claim(W,[e]) pays P*B/(S+B), the same as a wallet holding B all day.","severity":"low","title":"Rewards weight is a single, known snapshot second per epoch, so a buyer can hold only across the snapshot and take a share of that day's pot"}],"hash":"4c99ffd5de5c9b86b3558de43b1162a18d4d8277cfa7b9e8c533f5349a0fd832","nodeId":"e910c260-0167-4152-8022-4988fa5969aa","outcome":"completed","summary":"I added 29 tests in three new files under `test/`. I found one high-severity bug in the hook that tests can't work around, so I reported it with a failing proof instead of writing a test that accepts it. The full suite passes with the verifier's seed: 109 tests, 0 failures, in about 42 seconds.\n\n**Not committed.** The commit failed because git has no author name or email set on this machine, and I didn't change your git config. The three test files are staged in the working tree. `.imd-findings.json` is in the repository root as the brief asks, but git ignores it (not through the repo's own `.gitignore`), so it isn't staged.\n\n**New tests**\n- **`test/DeezRewardsInvariant.t.sol`**: random sequences against the reward distributor (256 runs, depth 60). Holders move $DEEZ, including into the pool and the burn address. $IMD arrives with and without `accrue`, time jumps (sometimes past the 365-day claim window), and anyone claims, finalizes or recycles. After every sequence it checks:\n  - the contract holds at least what it has reserved, and the reserve equals what the open epochs still owe;\n  - no epoch pays out more than it holds;\n  - total $IMD is conserved, and the pool and burn address are never paid;\n  - no claim exceeds the holder's balance share;\n  - a closed epoch never reopens, and the supply never changes.\n- **`test/DeezHookInvariant.t.sol`**: random buys and sells of all four kinds, harvests, donations and time jumps on a live pool, with $DEEZ as either currency of the pool (64 runs, depth 40). It checks that each swap's fee is 2% of the $IMD that changed hands (to within 1 wei), and that every fee is either pending or harvested. It also checks the pending fees match what the hook is owed, harvested $IMD sits in the distributor, and the hook and distributor never hold $DEEZ.\n- **`test/DeezFailurePaths.t.sol`**: 26 edge-case tests, including:\n  - the exact second the claim window closes;\n  - a batch of claims that contains a duplicate or an unfinished day (the whole batch reverts and nothing is paid);\n  - a transfer in the snapshot second versus the second after it;\n  - a day where no balance counts toward rewards (that day's pot is recovered later by recycling);\n  - shares that round down to zero;\n  - a donation that nobody accrued;\n  - claims and recycles on a closed day;\n  - wrong pool keys, callbacks before the pool exists, callers other than the pool manager, tiny \"dust\" swaps, and a second harvest;\n  - a fuzz test showing that all holders together never take more than the pot (at most 1 wei of rounding left per holder).\n\n**Findings in `.imd-findings.json`**\n1. **High, `src/DeezHook.sol:201`.** When $IMD is the amount the trader specifies (exact-input buys, exact-output sells), the 2% fee is worked out from the amount requested, not the amount that actually traded. If a swap stops early at the trader's price limit, the trader still pays the full fee.\n   - A buy of 10,000 IMD that stopped at its limit paid about 225 IMD in total, 200 of it in fees.\n   - An exact-output sell left the seller **paying** about 179 IMD on top of the $DEEZ they sold.\n\n   The proof is `test/scratch/PartialFillFee.t.sol`, embedded in the findings file. Both of its tests fail on the current code.\n2. **Medium, `DeezRewards.claim`.** Anyone can claim on behalf of any holder, so anyone can push the swarm distributor's share (up to 10% of supply) into a contract that can't move $IMD. The README says that share gets recycled to holders after the claim window; this stops that from happening.\n3. **Medium, `DeezLiquidityLock.lock()`.** It uses the current price with no limit, and the README tells the requester to transfer the tokens and call `lock()` in two separate transactions. Someone can move the price in between, lock the requester's 10M $DEEZ at that price, then buy it back cheaply.\n4. **Low.** Rewards are based on one publicly known snapshot second per day, so a wallet can buy just before it, sell just after, and collect a full day's share. It o","treeHash":"5a85e02b0d2b90535512278d0ca5290ee0262a82","usage":{"cachedInputTokens":3292386,"inputTokens":68,"model":"claude-opus-5-5","outputTokens":48028,"runtime":"claude","turns":35,"wallClockMs":912512}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"74979c69cdd583ad","findings":[{"citation":"resolved","description":"Branch asymmetry between the two fee paths. When $IMD is the unspecified side, afterSwap computes the fee from the executed BalanceDelta (_unspecifiedFee). When $IMD is the specified side (exact-input buy, exact-output sell), beforeSwap fixes the fee from params.amountSpecified before the swap runs, and afterSwap mints that same fixed figure (_specifiedFee) regardless of how much the swap filled. v4 stops a swap at sqrtPriceLimitX96 and refunds the unconsumed specified amount to the trader, but the hook's beforeSwap delta is not reduced, so the trader still pays 2% of the full requested input. Every router passes a price limit for slippage protection, so any exact-input buy that hits its limit (or exhausts the single-sided launch liquidity) is overcharged; the same applies to exact-output sells, where the hook keeps 2/98 of the requested output even if less was delivered. This breaks the brief's 'exactly 2% on every buy and every sell' and takes the trader's funds without a trade.","line":206,"path":"src/DeezHook.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 {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.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 {IUnlockCallback} from \"v4-core/src/interfaces/callback/IUnlockCallback.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 {TickMath} from \"v4-core/src/libraries/TickMath.sol\";\nimport {DeezToken} from \"src/DeezToken.sol\";\nimport {DeezHook} from \"src/DeezHook.sol\";\nimport {HookFlags} from \"src/HookFlags.sol\";\n\ncontract PImd is ERC20 {\n    constructor() ERC20(\"IMD\", \"IMD\") {}\n    function mint(address to, uint256 a) external { _mint(to, a); }\n}\n\ncontract PRouter is IUnlockCallback {\n    IPoolManager immutable m;\n    constructor(IPoolManager m_) { m = m_; }\n    function swap(PoolKey memory key, SwapParams memory p) external returns (BalanceDelta d) {\n        d = abi.decode(m.unlock(abi.encode(uint8(1), key, p)), (BalanceDelta));\n    }\n    function add(PoolKey memory key, ModifyLiquidityParams memory p) external {\n        m.unlock(abi.encode(uint8(2), key, p));\n    }\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        uint8 k = abi.decode(data, (uint8));\n        BalanceDelta d; PoolKey memory key;\n        if (k == 1) { SwapParams memory p; (, key, p) = abi.decode(data, (uint8, PoolKey, SwapParams)); d = m.swap(key, p, \"\"); }\n        else { ModifyLiquidityParams memory p; (, key, p) = abi.decode(data, (uint8, PoolKey, ModifyLiquidityParams)); (d,) = m.modifyLiquidity(key, p, \"\"); }\n        _s(key.currency0, d.amount0()); _s(key.currency1, d.amount1());\n        return abi.encode(d);\n    }\n    function _s(Currency c, int128 a) private {\n        if (a < 0) { m.sync(c); IERC20(Currency.unwrap(c)).transfer(address(m), uint128(-a)); m.settle(); }\n        else if (a > 0) m.take(c, address(this), uint128(a));\n    }\n}\n\n/// An exact-input buy that the price limit stops early is charged 2% of the requested input, not of what it paid.\ncontract PartialFillFeeTest is Test {\n    uint160 constant FLAGS = HookFlags.BEFORE_INITIALIZE | HookFlags.BEFORE_SWAP | HookFlags.AFTER_SWAP\n        | HookFlags.BEFORE_SWAP_RETURN_DELTA | HookFlags.AFTER_SWAP_RETURN_DELTA;\n\n    function test_partialFillBuyChargedOnRequestedInput() public {\n        PoolManager manager = new PoolManager(address(this));\n        DeezToken token = new DeezToken();\n        PImd imd = new PImd();\n        bytes memory code = abi.encodePacked(type(DeezHook).creationCode, abi.encode(address(manager), address(token), address(imd)));\n        DeezHook hook;\n        for (uint256 i; i < 300_000; i++) {\n            address pred = address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), bytes32(i), keccak256(code))))));\n            if (!HookFlags.matches(pred, FLAGS)) continue;\n            hook = new DeezHook{salt: bytes32(i)}(IPoolManager(address(manager)), address(token), address(imd));\n            break;\n        }\n        bool t0 = address(token) < address(imd);\n        PoolKey memory key = t0\n            ? PoolKey(Currency.wrap(address(token)), Currency.wrap(address(imd)), 12_500, 60, IHooks(address(hook)))\n            : PoolKey(Currency.wrap(address(imd)), Currency.wrap(address(token)), 12_500, 60, IHooks(address(hook)));\n        manager.initialize(key, TickMath.getSqrtPriceAtTick(0));\n\n        PRouter r = new PRouter(manager);\n        token.transfer(address(r), 1_000_000 ether);\n        imd.mint(address(r), 10_000_000 ether);\n        r.add(key, ModifyLiquidityParams(-6000, 6000, 1_000_000 ether, 0));\n\n        // Buy DEEZ with exact 1,000,000 IMD in, but with a price limit 60 ticks away (normal slippage protection).\n        bool zeroForOne = !t0;\n        uint160 limit = TickMath.getSqrtPriceAtTick(zeroForOne ? int24(-60) : int24(60));\n        uint256 imdBefore = imd.balanceOf(address(r));\n        r.swap(key, SwapParams(zeroForOne, -1_000_000 ether, limit));\n        uint256 paid = imdBefore - imd.balanceOf(address(r));\n        uint256 fee = hook.pendingFees();\n        emit log_named_uint(\"trader paid\", paid);\n        emit log_named_uint(\"hook fee\", fee);\n        // Advertised: exactly 2% of what the trader pays.\n        assertLe(fee, paid * 200 / 10_000 + 1, \"fee exceeds 2% of what the trader paid\");\n    }\n}","reproduction":"Fresh PoolManager, pool initialized at tick 0, liquidity 1e24 in [-6000,6000]. Buy DEEZ exact-input 1,000,000 IMD with sqrtPriceLimit at tick ±60 (the IMD->DEEZ direction). The swap fills ~3,042 IMD, but the hook still takes 20,000 IMD: the trader pays 23,042 IMD in total, and the fee is 86.8% of that. Expected: fee = 2% of the IMD the trader actually pays (~460 IMD). Fix: charge the specified-side fee in afterSwap from the executed delta (as the unspecified path already does, e.g. as an unspecified/afterSwap delta), or make beforeSwap's delta proportional and reject/repair partial fills. The test passes once fee <= 2% of paid.","severity":"high","snippet":"            return uint256(-params.amountSpecified) * FEE_BPS / BPS;","title":"Specified-side fee is charged on the requested amount, not the executed amount: a price-limited (partially filled) swap pays far more than 2%"}],"hash":"5a072abd2be6c8010b9448909fd602a5c1099f5cd3c1111657e7c9f2ea081914","nodeId":"e9bafb4b-71b7-48e1-8235-84d96d2b2907","outcome":"completed","summary":"I found one high-severity defect and wrote it to `.imd-findings.json`, with its proof test and a coverage row for all 13 entry points. I changed no source files, and the scratch test directory is deleted.\n\n**Finding 1 (high): swaps stopped by their price limit are charged far more than 2%** (`src/DeezHook.sol:206`)\n- **What's wrong:** for an exact-input buy or an exact-output sell, the hook sets its fee in `beforeSwap` from the amount the trader *asked* to swap. It never adjusts that fee to what actually traded. If the swap stops early at its price limit, the trader gets the unused input back but still pays the full fee. Routers always set a price limit for slippage protection, so this is a normal case. It breaks the brief's \"exactly 2% on every buy and every sell\".\n- **Reproduction (the proof test fails on the current code as expected):** an exact-input buy of 1,000,000 IMD with a price limit 60 ticks away fills only about 3,042 IMD. The hook still takes 20,000 IMD, so the trader pays 23,042 IMD and 87% of it is fee. The fee should be about 460 IMD.\n- **Fix:** work out this side's fee from what actually traded, in `afterSwap`, the same way the other two swap cases already do.\n\n**Coverage:** 12 entry points hold; `DeezHook.afterSwap` is marked `finding` (it records the oversized fee).\n- **Access control:** every callback that should only accept the PoolManager checks for it. `beforeInitialize` accepts one pool only, with the right currency pair and any fee tier. No contract has an owner or admin setter.\n- **Asymmetry:** I compared the specified-side and unspecified-side fee paths (the source of finding 1), and the `claim` / `recycle` / `accrue` bookkeeping.\n- **Trust gap:** the only payout paths are rewards paid to the holder's own address and the permanent liquidity lock.\n\n**Not reported (no concrete reproduction):**\n- DEEZ held as Uniswap claims inside the PoolManager is excluded from rewards along with the pool's liquidity.\n- Any DEEZ left in `DeezLiquidityLock` still counts toward the eligible supply.\n- A DEEZ pool on another venue (e.g. Uniswap v2) could be paid rewards that anyone can then take from it.\n\nI didn't run a full pass on the reward-math edge cases.","treeHash":null,"usage":{"cachedInputTokens":577879,"inputTokens":18,"model":"claude-opus-5-5","outputTokens":9510,"runtime":"claude","turns":10,"wallClockMs":125055}},{"artifacts":[],"attempt":1,"bundleHash":"683ea8f19ebb6f0dc65ba78c09b341c2740627f0798ee08284ee9eb53cd4ce3d","device":"d0653dc91b6e2259","findings":[],"hash":"6ee3f5142d3b76497897138a1b045c55dc3436e7146c1990fc60b706c8aecc3b","nodeId":"2af88f07-4b54-4956-8669-a894d6db9995","outcome":"completed","summary":"The manifest is updated and verified. Only launch.json changed.\n\n**What I found**\n- The hook, token, pool fields and constructor arguments in the existing launch.json already matched the accepted code: `DeezHook(poolManager_, token_, pairedCurrency_)` written as `$poolManager`, `$token`, and the IMD address the launch names, and the five permissions match `getHookPermissions` in `src/DeezHook.sol:108` exactly (flags 0x20CC).\n- The notes were stale. They still described the round-0 design that the round-1 revision replaced: Trace208 balance checkpoints, third-party claims, a 365-epoch claim window, and harvest-timed epochs.\n\n**What I changed**\n- Rewrote only the `notes` string to describe the accepted implementation: cumulative balance checkpoints with epoch-average weighting, fees recorded per epoch and harvested by `harvest()` or `harvestEpochs`, the `PartialFill` revert on partly filled IMD-specified swaps, `claim` callable only by the account itself, a 90-epoch recycle window, and the lock's `lockAs` modes.\n- Notes length is 3672 characters, under the 4000 cap. `\"kind\": \"univ4_hook\"` remains the first field. All other fields are untouched.\n\n**Verification**\n- `forge build` succeeds.\n- `forge test` with the verifier's fuzz seed: 113 passed, 0 failed, 0 skipped across 7 suites.\n- `git status` shows launch.json as the only modified file.","treeHash":"c74900a2aa128cd91a5d8e02f62a588c4fc435c5","usage":{"cachedInputTokens":624536,"inputTokens":322,"model":"claude-fable-5-1","outputTokens":6401,"runtime":"claude","turns":11,"wallClockMs":190292}},{"artifacts":[],"attempt":1,"bundleHash":"ad9d011b13c7a80e843d80b4094bf819dd1465ee6c9a8985ee769b0dad998b64","device":"20e8e883cb88740a","findings":[],"hash":"802ff802c001e6a15b56463b7e947d847ee9be7cce43caaeb48f0891470291af","nodeId":"d3e365b3-3ffa-43a2-aec2-31360ff75da0","outcome":"completed","summary":"The IMDeezNutz launch is built, tested and documented. The build, the full test suite with the verifier's fuzz seed, and the root format check all pass.\n\n**What was delivered**\n\n- `src/DeezToken.sol`: fixed-supply ERC-20 named IMDeezNutz, symbol DEEZ, one billion tokens minted once to the deployer. No owner, mint, pause or tax. Its one addition is a timestamp-keyed balance history, so rewards can be weighted by real balances without staking or registration.\n- `src/DeezHook.sol`: the Uniswap v4 hook. It takes exactly 2% of the IMD side of every buy and sell, in all four swap shapes, and mints the fee to itself as an ERC-6909 claim so the first buy on a DEEZ-only pool works and no recipient can ever break a swap. Anyone can call `harvest()` to move the IMD into the distributor. It accepts one pool only, has no owner, and has no liquidity callbacks, so the factory's fee collect is untouched.\n- `src/DeezRewards.sol`: created by the hook's constructor. Daily epochs, share equals pot times your balance at the day's end divided by eligible supply. The pool, the burn address, the hook and the distributor are excluded. Anyone can claim for anyone, and unclaimed shares recycle after a year.\n- `src/DeezLiquidityLock.sol`: also created by the hook. Send DEEZ or IMD and call `lock()` to add liquidity nothing can withdraw. This is how the requester's 1% becomes permanent liquidity after launch.\n- `site/`: a static single page in imd.fun's style (IBM Plex Mono, black background, 1.5px rules), with copyable addresses, the flywheel explanation, the math, an in-page claim and harvest widget, and explorer, Uniswap and DexScreener links. Addresses are filled in through `site/deez.config.js` after launch.\n- `README.md`: manifest parameters, fee and reward math, constants chosen, after-launch steps, what the brief asked that the launch itself handles, and security notes.\n\n**Checks**\n\n| Check | Result |\n| --- | --- |\n| `forge build` (solc 0.8.26, cancun, via_ir, bytecode_hash none) | pass |\n| `forge test` with the verifier's fuzz seed | 80 passed, 0 failed |\n| `forge fmt --check` | pass |\n| Pinned floor suites rehearsed against the built bytecode | 13 passed |\n\n**Deviations and assumptions to note**\n\n- Rewards stay in IMD rather than being sold for ETH, because the brief asks for IMD payouts. I verified on mainnet that IMD at the given address is a standard OpenZeppelin ERC-20 (LayerZero OFT), so taking it out of the PoolManager works.\n- The hook takes three constructor arguments: `$poolManager`, `$token`, and the IMD address. Nothing has an owner, so `$owner` is only the wallet receiving the 2%.\n- The swarm's Merkle distributor is not excluded from the eligible supply since its address is unknown at build time. Its share is recycled to holders after the claim window.\n- Dependencies are vendored as plain files in `lib/` with git metadata removed. Two nested `.gitmodules` files inside `lib/` could not be deleted because the sandbox mounts them read-only. They are inert, since git only reads the top-level one, and there are no `.git` directories.\n- Slither was not available here. Nothing was committed, since `.git/` is off limits and committing was not requested.","treeHash":"75c928605600b603cd41c46ebb2c2b558d0ab88d","usage":{"cachedInputTokens":4153803,"inputTokens":962,"model":"claude-fable-5-1","outputTokens":102493,"runtime":"claude","turns":57,"wallClockMs":1609168}},{"artifacts":[],"attempt":1,"bundleHash":"231616f92e1bcfa1a74add7fbad0c74c1dd5baa11468385f4e331e3ad11737a7","device":"fb26479062458645","findings":[],"hash":"a2417343e24aba98ee546ffd06d1aa65456c15c70dbb56ecd3a64642679ad318","nodeId":"e910c260-0167-4152-8022-4988fa5969aa","outcome":"completed","summary":"I've updated my two test files for the new claim logic, and the full suite passes: **157 tests in 13 suites, 0 failures**, run with the verifier's fuzz seed. I found no new defect, so I didn't write a `.imd-findings.json`.\n\n**Why the changes were needed:** in the last round the implementer replaced the `claimed` flag with `paidOf`. Claims now pay whatever has been added to an epoch's pot since the last claim, and `claim` collects the hook's not-yet-collected fees first by calling `hook.harvestEpochs`. My `test/DeezFailurePaths.t.sol` no longer compiled. My tests had also used a plain address as the hook, which `claim` now has to call, so they now use the existing `MockHook`.\n\n**`test/DeezFailurePaths.t.sol`**\n- **Changed:** the old test expecting a duplicate epoch in one claim to revert now expects it to pay once. A repeat claim with nothing new reverts with `NothingToClaim`.\n- **New tests:**\n  - Fees that arrive after some holders have claimed still split exactly by balance. Early claimers are topped up to their share of the full pot, the pot ends fully paid out, and nothing stays reserved.\n  - Late fees for an epoch whose leftovers were already moved on after the 90-day window go to the current epoch's pot. A claim on the closed epoch reverts and collects nothing.\n  - A fuzz test (512 runs) checks that in whatever order holders claim, before or after late fees arrive, each one ends with exactly `(early + late) × weight / supply`.\n  - On a real PoolManager pool: a claim covering one finished day and the day still running reverts as a whole. The hook keeps its pending fees and its ERC-6909 claims. A later claim of the finished day alone pays the right share and leaves the running day's fees with the hook.\n\n**`test/DeezRewardsInvariant.t.sol`**\n- The random-sequence handler now uses `MockHook` and has two more actions: adding late fees to past or current epochs, and anyone triggering a harvest.\n- The per-claim checks now allow an ended pot to grow, but only by the fees collected for it. They also check that the total paid to an account for an epoch never exceeds its balance share.\n- **New invariants:**\n  - The pending fees the test tracks match what the hook holds.\n  - Each pot's payouts equal the sum of what holders were paid from it, and no holder got more than its share.\n- About 15,000 calls, all invariants pass, no reverts in the handler.\n\nOne limit, not a reported defect: amounts above 2¹²⁸ wei (about 3.4×10²⁰ tokens) would be silently cut down in `accrue` and `credit`. That is far beyond any realistic $IMD amount, so I didn't test or report it.\n\nI haven't committed the changes; they're in the working tree for the harness to pick up.","treeHash":"7db7d7a307a5437aa122b77298d056f637e77526","usage":{"cachedInputTokens":938893,"inputTokens":28,"model":"claude-opus-5-5","outputTokens":15817,"runtime":"claude","turns":14,"wallClockMs":428101}},{"artifacts":[],"attempt":1,"bundleHash":"df53145a9d0b8bbdb2caebf25e2d61597aede6402869a3f51bd9cc30c98487ec","device":"0cf632e317dfab7a","findings":[],"hash":"a5abbeb31c773fa5ccf67944a6055f27a35834c5b77f8f9716ad01f9db0475be","nodeId":"d3e365b3-3ffa-43a2-aec2-31360ff75da0","outcome":"completed","summary":"The finding reproduced and is fixed. The reviewer's proof fails on the starting tree and passes now, and the full suite, formatting check and the three pinned floor suites all pass.\n\n**What was wrong.** With per-epoch crediting from round 1, fees traded after an epoch's last harvest only reached its pot at the next harvest. The distributor marked a claim with a one-shot flag, so a holder who claimed before that harvest could never collect its share of those fees. The proof showed alice receiving half of what she was owed.\n\n**What changed in `src/DeezRewards.sol`**, both layers the reviewer suggested:\n- `claim` now calls the hook's `harvestEpochs` for the epochs it pays before accruing and computing shares, so late fees are in the pot first.\n- The one-shot `claimed` flag is replaced by `paidOf[epoch][account]`. A share is the account's fraction of the pot minus what it was already paid, so a repeat claim pays the difference against a grown pot, and nothing otherwise.\n- `claimable` counts the hook's still-pending fees for the epoch and subtracts what was paid, so the view matches what a claim pays.\n- The `AlreadyClaimed` error is gone. A claim that pays nothing in total still reverts with `NothingToClaim`.\n\nThe hook itself changed only in comments. Its `harvestEpochs` already existed and is permissionless.\n\n**Tests.** A new `MockHook` in `test/mocks/` stands in for the hook in the distributor's unit tests. Added tests cover a claim harvesting the epoch's pending fees first, harvesting only the listed epochs, a late credit paid on a second claim, and the new repeat-claim semantics. The flywheel suite gains the proof's exact end-to-end scenario: alice is paid her full share in one claim, a later harvest returns zero, her second claim reverts, and bob gets a third of her amount.\n\n| Check | Result |\n|---|---|\n| Reviewer's proof on starting tree | failed as reported |\n| Reviewer's proof after fix | passed |\n| `forge build` | clean |\n| `forge test` with the verifier's fuzz seed | 117 passed, 0 failed |\n| `forge fmt --check` | passed |\n| Pinned floor suites (hook, owners, token) | 11 passed, 2 skipped as before |\n\n**Documentation.** The README's distributor section, after-launch notes, security notes, test summary and revision history describe the new claim semantics, including that a claim of an epoch with unharvested fees would revert if the pair token ever refused the transfer. The site's claim wording no longer tells holders to harvest first. The answer to the finding is in `.imd-responses.json` with verdict `fixed`.\n\nNot done: no deployment or on-chain action, which is IMD's deployer's step. The work was not committed, since the task did not ask for a commit.","treeHash":"e052d91423cf3d99cc7db7d0b42f024a7c242428","usage":{"cachedInputTokens":3475357,"inputTokens":994,"model":"claude-fable-5-1","outputTokens":36452,"runtime":"claude","turns":59,"wallClockMs":714157}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"4576f670afa0131d","findings":[{"citation":"resolved","description":"eligibleSupplyAt()/isExcluded() exclude only the PoolManager, 0x..dEaD, the hook and the distributor contract itself. The launch split the brief and README describe is 88% into the pool (PoolManager, excluded), 10% to the IMD swarm's MerkleDistributor (factory.distributorOf(launchNumber); its source is not in this tree, and nothing in the tree or README gives it a way to move $IMD, so the strand-in-contract part of this finding holds unless the distributor can sweep arbitrary tokens; the 83% dilution holds regardless) and 2% to the requester. So at launch the eligible supply is 120,000,000 DEEZ and the Merkle distributor's weight is 100,000,000 / 120,000,000 = 83.3% of every pot, falling only as swarm members withdraw their DEEZ. The README assumes that share is merely 'unclaimable and recycled to holders after the claim window'. It is not: claim(account, epochs) is callable by anyone for any non-excluded account and always pays `account`, so a single call claim(distributor, [e]) by any address moves the distributor's share of pot e into a contract with no function to move $IMD. That $IMD is lost for good; it is never recycled because pot.claimed already counts it. Even without that call, 5/6 of the trading fees sit idle for 365 epochs instead of reaching holders, which breaks the brief's 'All of this 2% ... must be accumulated and redistributed to all eligible holders'. The same push-to-contract loss applies to any contract holding DEEZ that cannot move $IMD (the lock contract is the one exception, since lock() consumes $IMD).","line":107,"path":"src/DeezRewards.sol","reproduction":"Scratch test test/scratch/Distributor.t.sol (DeezBase fixture, DEEZ is currency0): transfer 100,000,000 DEEZ to a contract with no $IMD withdrawal (a MockERC20 stands in for the MerkleDistributor), 20,000,000 DEEZ to requester 0xBEEF, the remainder to the PoolManager; buy with 1,000 IMD so the hook takes 20 IMD; hook.harvest(); warp to (epoch+1)*1 days; rewards.finalize(epoch). Observed: pot 20.000 IMD, claimable(distributor)=9.0909 IMD, claimable(requester)=1.8182 IMD (the fixture's swapper still held 100M DEEZ, which is why the distributor shows 45% here; with the real split and no other holder it is 16.67 of the 20 IMD). Then vm.prank(0xBAD); rewards.claim(distributor,[epoch]) succeeds and imd.balanceOf(distributor)=9.0909 IMD: paid into a contract that cannot spend or forward it, by a caller unrelated to it. Expected: the swarm allocation that is not yet in holders' wallets earns nothing (or its share stays recyclable), and nobody but the account (or a recipient it chose) can direct where its $IMD goes. Fix: pass \"$factory\" and \"$launchNumber\" to DeezHook and on to DeezRewards, and exclude IFactory(factory).distributorOf(launchNumber) in isExcluded()/eligibleSupplyAt() (read lazily at finalize/claim time, never in the constructor); additionally require msg.sender == account in claim() (or let the account name a recipient) so no third party can push $IMD into a contract that cannot use it.","severity":"high","snippet":"    function isExcluded(address account) public view returns (bool) {\n        return account == poolManager || account == BURN_ADDRESS || account == hook || account == address(this)\n            || account == address(0);\n    }","title":"Swarm Merkle distributor (10% of supply) is an eligible holder: it is weighted ~83% of every early pot, and permissionless claim() lets anyone strand that $IMD inside it"},{"citation":"resolved","description":"For an exact-input buy (amountSpecified = -in IMD) and an exact-output sell (amountSpecified = +out IMD) the hook returns the whole fee (in*2% or out*2/98) as a positive specified BeforeSwapDelta. v4 credits that delta to the hook and debits the trader regardless of how much of the swap the pool then fills (Hooks.afterSwap: swapDelta = swapDelta - hookDelta). afterSwap then mints the same _specifiedFee(params) as a claim and never looks at the realised delta on the specified side. Whenever the pool fills less than requested, which Pool.swap does without reverting when the price limit is reached or liquidity on that side runs out (the loop stops at sqrtPriceLimitX96 and returns the partial amount), the fee stays 2% of the request: a seller asking for more $IMD than the pool holds receives delivered - out*2/98, which is negative once delivered < 2.04% of the request, so the seller pays both $DEEZ and $IMD; a buyer whose price limit stops the swap early pays 2% of the full input for a sliver of output. On the launch pool, which is seeded with $DEEZ only and holds just the $IMD that buys bring in, 'more than the pool holds' is the ordinary state for an exact-output sell, and routers pass price limits as a matter of course. The 'exactly 2% on every buy and every sell' guarantee breaks and the surplus is paid to the holder pot, i.e. to the wrong party.","line":154,"path":"src/DeezHook.sol","reproduction":"Proof test test/scratch/ProofPartialFillFee.t.sol (fresh PoolManager, DeezToken, mock IMD, the hook mined onto its flags, pool at 1:1, fee 12500, spacing 60, 1e21 liquidity of DEEZ only in [60, maxUsableTick] as the factory seeds it). Case A: buy with 1 IMD exact input so the manager holds exactly 1 IMD; then swap DEEZ->IMD exact output 100 IMD with the usual MIN/MAX price limit. Observed: hook.pendingFees grew by 2.0408 IMD (=100*2/98), the trader's IMD delta is -1.0731 IMD (they paid IMD on a sell) and their DEEZ delta is -0.9732 DEEZ; the pool delivered 0.9677 IMD so the fee is 211% of the gross. Case B: buy with 1,000 IMD exact input and sqrtPriceLimit = sqrtPriceAtTick(61). Observed: the pool consumed 0.0508 IMD, the trader paid 20.0508 IMD in total, of which 20 IMD is the hook fee (2% of the 1,000 specified), for 0.0498 DEEZ: a 99.7% fee. Expected in both: fee <= 2% of the $IMD that actually changed hands, or the partially filled swap refused. The proof asserts exactly that and accepts a revert; it fails on this code with 'trader paid IMD on a sell' and 'hook fee exceeds 2% of the gross IMD moved'. Minimal fix: in afterSwap, when $IMD is the specified currency, compare the realised specified amount with what beforeSwap assumed (exact input: |delta.specified| == in - fee; exact output: delta.specified == out + fee) and revert (e.g. PartialFillNotSupported) when the pool filled less; or move the whole fee to afterSwap by computing it from the realised delta (the specified-side part can then only be taken by shrinking what is minted and refunding the surplus hook delta to the swapper via take(), which needs the trader's address, so the revert is the smaller change).","severity":"high","snippet":"        uint256 fee = _specifiedFee(params);\n        return (IHooks.beforeSwap.selector, toBeforeSwapDelta(fee.toInt128(), 0), 0);","title":"Fee on $IMD-specified swaps is fixed in beforeSwap from the requested amount: a partially filled swap is charged far more than 2%, and an exact-output seller can end up paying $IMD"},{"citation":"resolved","description":"lock() reads both balances and chooses: token0-only range when balance1 == 0, token1-only when balance0 == 0, otherwise a full-range position whose liquidity is min(l0, l1). The README's instruction for the requester's mandatory 1% lock is 'transfer >= 10,000,000 DEEZ to hook.liquidityLock() and call lock()'. Between the transfer and the call (or by front-running the call) anyone can send 1 wei of $IMD to the lock: balance1 != 0, so the full-range branch runs, l1 for 1 wei is 1, and the position adds liquidity 1 consuming 1 wei of each token. The DEEZ stays in the lock contract, which has no withdraw function and is not a pool position. The attacker can repeat this for every lock() attempt at the cost of 1 wei plus gas, so the brief's 'lock it permanently' requirement cannot be met against a persistent griefer except through a private bundle. The same branch also strands the larger side when the requester sends both tokens in any ratio other than the pool's: only the min(l0, l1) position is added; the 1 ppm shave leaves dust on the limiting side so the next call is again full range and only after several more calls (the dust converges to zero, observed in 7 calls) can the leftover be locked single-sided. Slither's incorrect-equality hits on these lines are this defect.","line":76,"path":"src/DeezLiquidityLock.sol","reproduction":"Scratch test test/scratch/LockGrief.t.sol (DeezBase fixture, balanced 1e21 liquidity at 1:1, DEEZ is currency0): token.transfer(lock, 10_000_000 ether); imd.mint(lock, 1) as the attacker; lock.lock(). Observed: returned liquidity 1, totalLocked0 = 1 wei, token.balanceOf(lock) = 9,999,999.999999999999999999 DEEZ still sitting in the lock contract. Expected: the 10,000,000 DEEZ become a position above the current price (the test's own seedTokenOnly-style range) regardless of dust on the other side. Second case in the same file: 10,000,000 DEEZ + 1,000 IMD, lock() leaves 9,999,000 DEEZ and 0.001 IMD in the contract; six more lock() calls are needed before the IMD dust reaches zero and the DEEZ finally locks single-sided. Fix: make lock() lock everything it holds in one call: add the balanced full-range position for min(l0, l1), then put the remaining balance of the larger side into its single-sided range above/below the price in the same unlock (or at least treat a side below a small threshold as absent, and let the caller choose the mode explicitly).","severity":"medium","snippet":"        if (balance1 == 0) {\n            // Token0 only: the whole position must sit above the current price.\n            tickLower = _ceilToSpacing(tick + 1, spacing);\n            if (tickLower >= tickUpper) tickLower = tickUpper - spacing;\n        } else if (balance0 == 0) {","title":"DeezLiquidityLock picks the position mode from balance presence: 1 wei of the other token sent by anyone turns a single-sided lock of 10,000,000 DEEZ into a full-range position of liquidity 1"},{"citation":"resolved","description":"accrue() credits everything received since the last call to currentEpoch(), and shares are decided by getPastBalanceOf at snapshotOf(e) = the last second of the day. Fees are pooled as pendingFees in the hook until anyone calls harvest(), and recycle() likewise drops a closed epoch's remainder into the current epoch. Nothing ties a reward to the time a balance was held: a wallet that holds DEEZ only across the day boundary gets the same share as one that held all day, and the actor who times harvest()/recycle() chooses which day's snapshot the money is weighted by. A trader can therefore buy a large position in the last block of a UTC day, call harvest() (and recycle() for any epoch past its window) in that block to credit the accumulated fees to that day, hold one block, sell, and claim the pot share the next day, paying only the round-trip fees. Holders who held through the trades that generated the fees receive less than the brief's 'weighted strictly by balance' implies.","line":147,"path":"src/DeezRewards.sol","reproduction":"State: 5,000 IMD of un-harvested pendingFees on the hook (nobody has called harvest() for a while), eligible supply 120,000,000 DEEZ. At block.timestamp = (e+1)*86400 - 1 (last second of epoch e) attacker buys 30,000,000 DEEZ (cost: 2% hook fee + 1.25% LP fee + slippage, roughly 3.3% + impact), then calls hook.harvest(): pots[e].total += 5,000 (plus its own buy fee). At (e+1)*86400 the attacker sells back (another ~3.3%) and calls rewards.claim(attacker, [e]): payout = 5,000 * 30M / 150M = 1,000 IMD for a few seconds of holding, while every long-term holder's share of those fees shrank by 20%. Expected: a share proportional to balance held over the period the fees were earned, or at least fees credited to the epoch in which they were traded (accrue in afterSwap per epoch) so the harvest timing cannot be chosen. Fix: record the epoch at fee time (credit pendingFees per epoch in afterSwap and let harvest() move each epoch's amount to its own pot, allowing harvests into ended epochs) and/or weight by time-averaged balance (Checkpoints already give the history to integrate over the epoch).","severity":"low","snippet":"        uint256 epoch = currentEpoch();\n        pots[epoch].total += uint128(amount);","title":"Rewards are credited to the epoch of the permissionless harvest and weighted by a one-second balance snapshot, so a one-block holder at the day boundary captures the whole accumulated pot"}],"hash":"b3554dce1ff4def2000b169c5b5b6959edac6e214728d58bb9a6e8dda9f140d2","nodeId":"4c43587b-b355-4cc6-bc7f-ecc413a3caac","outcome":"completed","summary":"The review is complete. The findings file is valid JSON with four findings and sixteen coverage rows, and no source, test or configuration file was changed. The only writes are `.imd-findings.json` and scratch tests under `test/scratch/`, which is gitignored and discarded. Git itself cannot read the seat's global config inside the sandbox, so I could not run a status listing, but nothing outside those two locations was written.\n\n**Findings, most severe first**\n\n1. **High, DeezRewards.sol:107.** The swarm's Merkle distributor is not excluded from eligible supply. With the launch split (88% in the PoolManager, 10% in the distributor, 2% to the requester), the distributor is weighted 100M of 120M, so about 83% of every early pot. Because `claim` is permissionless and pays the named account, anyone can push that share into the distributor contract, where it is lost rather than recycled as the README assumes. A scratch test moved 9.09 of a 20 $IMD pot into a stand-in contract from an unrelated caller. Fix: exclude `factory.distributorOf(launchNumber)` via `$factory` and `$launchNumber`, and restrict `claim` to the account or a recipient it names.\n\n2. **High, DeezHook.sol:154, with proof.** The fee on $IMD-specified swaps is fixed in `beforeSwap` from the requested amount, so a partially filled swap is overcharged. An exact-output sell asking for 100 $IMD from a pool holding 1 $IMD was charged 2.04 $IMD and the seller ended up paying 1.07 $IMD on a sell. An exact-input buy stopped by a price limit paid a 20 $IMD fee for 0.05 $IMD consumed. The self-contained proof fails on the current code for both shapes and accepts either a revert on partial fills or a fee computed from the realised delta.\n\n3. **Medium, DeezLiquidityLock.sol:76.** The lock chooses its position mode from balance presence. One wei of $IMD sent by anyone turned a 10M $DEEZ single-sided lock into a full-range position of liquidity 1, leaving the $DEEZ in the contract. Sending both tokens in any other ratio needs seven further calls before the leftover can be locked. Fix: lock the balanced part and both leftovers in one call.\n\n4. **Low, DeezRewards.sol:147.** Fees are credited to the epoch of the permissionless harvest and weighted by a one-second snapshot, so a one-block holder at the day boundary who times `harvest` captures the accumulated pot.\n\n**Coverage.** All thirteen listed entry points have a row. Eight hold, including both `unlockCallback`s, `beforeInitialize`, `harvest`, `finalize`, `recycle` and the three token functions. Three invariant rows also hold: the hook's delta nets to zero per swap, pending fees equal the hook's claims, and the rewards balance covers what is reserved. The Slither leads on strict equality in `lock` are finding 3; the other static-analysis lines reproduced nothing.","treeHash":null,"usage":{"cachedInputTokens":1616744,"inputTokens":386,"model":"claude-fable-5-1","outputTokens":59479,"runtime":"claude","turns":32,"wallClockMs":935664}},{"artifacts":[],"attempt":1,"bundleHash":"eafb5a48e05d34c5f58f81abba0f9c721836d7f4b43544ece65e402402f077d8","device":"27168b1ff68f84d2","findings":[],"hash":"c766cb8e9174d2345adbde115f8f64223ffbee1d67d0ec21716bbb62aab6054c","nodeId":"2af88f07-4b54-4956-8669-a894d6db9995","outcome":"completed","summary":"The manifest is revised and verified. Only `launch.json` changed.\n\n**What I did**\n- Kept the hook, token and pool sections as they were. They were checked against the source and are correct: `DeezHook` with constructor arguments `$poolManager`, `$token` and the IMD pair address in declaration order, and the five permissions that `getHookPermissions` returns (beforeInitialize, beforeSwap, afterSwap, beforeSwapReturnDelta, afterSwapReturnDelta). Pool fields are the launch facts: IMD pair, fee 12500, tick spacing 60, the one-to-one initial price.\n- Rewrote only the notes so they describe the accepted round-2 implementation. The previous notes predated the change where `claim` harvests the epoch's still-pending fees through the hook before paying, and `paidOf` replaces the one-shot claimed flag so a repeat claim pays only what the pot gained. The notes now say that, and that `claimable` counts the hook's pending fees. Everything else in the notes is unchanged.\n\n**Checks**\n- JSON parses, `\"kind\": \"univ4_hook\"` is the first field, and the top-level keys are only kind, hook, token, pool and notes.\n- Notes are 3902 characters, under the 4000 cap.\n- `forge build` passes and `forge test` with the verifier's fuzz seed passes all 117 tests.\n- `git status` shows launch.json as the only modified file. No configuration, lib or workflow file was touched.\n\n**Open point for the reviewers**\nThe third constructor argument is the IMD token address itself, which the launch facts give as the pair currency. It is not a stand-in and no placeholder covers it, so it is written literally.","treeHash":"870bc7b125f8ca4bd58e93db619066fe2ad65245","usage":{"cachedInputTokens":379401,"inputTokens":226,"model":"claude-fable-5-1","outputTokens":5056,"runtime":"claude","turns":9,"wallClockMs":150513}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"7f1df2a55552de06","findings":[{"citation":"resolved","description":"claim() is callable by anyone for any account and always pays `account`. The README (lines 87, 183-185) relies on unclaimed shares of non-claiming holders, notably the swarm's Merkle distributor holding 10% of supply, being recycled to real holders after CLAIM_WINDOW. But any third party can call claim(distributor, epochs) and push the IMD into a contract that has no function to move IMD, so it is frozen forever and recycle() can never return it (the share is counted in pot.claimed). The same applies to DeezLiquidityLock dust, the factory, and any DEEZ-holding contract. Up to 10% of every epoch's fees (the swarm's share alone) is lost rather than redistributed to eligible holders.","line":166,"path":"src/DeezRewards.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 {DeezToken} from \"src/DeezToken.sol\";\nimport {DeezRewards} from \"src/DeezRewards.sol\";\n\ncontract Imd is ERC20 {\n    constructor() ERC20(\"IMD\", \"IMD\") {}\n    function mint(address to, uint256 a) external { _mint(to, a); }\n}\n\n/// @dev Stands in for the swarm's Merkle distributor: holds DEEZ, has no way to move any other token.\ncontract Distributor {}\n\ncontract ClaimForContractTest is Test {\n    function test_thirdPartyCannotStrandShareOfContractHolder() public {\n        vm.warp(10 days);\n        DeezToken token = new DeezToken();\n        Imd imd = new Imd();\n        DeezRewards rewards = new DeezRewards(address(token), address(imd), address(0xBEEF1), address(0xBEEF2));\n        Distributor dist = new Distributor();\n        address holder = address(0xA11CE);\n        token.transfer(address(dist), 100_000_000 ether); // swarm 10%\n        token.transfer(holder, 900_000_000 ether);\n\n        imd.mint(address(rewards), 1000 ether);\n        rewards.accrue();\n        uint256 epoch = rewards.currentEpoch();\n        vm.warp(block.timestamp + 2 days);\n\n        // Any third party pays the distributor's share to a contract that can never move it.\n        uint256[] memory epochs = new uint256[](1);\n        epochs[0] = epoch;\n        vm.prank(address(0xBAD));\n        try rewards.claim(address(dist), epochs) {} catch {}\n\n        // The holder ends up with the full 10% that a contract which can't spend it would otherwise freeze.\n        assertEq(imd.balanceOf(address(dist)), 0, \"IMD stranded in non-claiming contract\");\n    }\n}","reproduction":"Distributor contract holds 100M DEEZ, holder 900M. 1000 IMD accrued in epoch e. After e ends, address 0xBAD calls claim(distributor,[e]): 100 IMD transferred to distributor, which cannot spend it; recycle(e) after 366 epochs only recycles 0 of that share. Expected: a third party cannot direct a share to an account that did not ask for it (e.g. require msg.sender==account, or claim only to EOA/opt-in), so the unclaimed 100 IMD is recycled. Actual: 100 IMD permanently stranded. Proof test fails with 'IMD stranded in non-claiming contract: 100e18 != 0'.","severity":"high","snippet":"    function claim(address account, uint256[] calldata epochs) external nonReentrant returns (uint256 total) {","title":"Anyone can claim on behalf of a contract holder (swarm Merkle distributor, lock, CEX/other pools) and permanently strand its IMD share instead of letting it be recycled"},{"citation":"resolved","description":"Weight is the balance at one second (last second of the UTC day), not time-weighted. A trader buys DEEZ in the last block before snapshotOf(e) and sells in the first block of e+1, receiving pots[e].total * bal/eligibleSupply for a ~12s holding while long-term holders are diluted. Round-trip cost is ~2x(2% hook + 1.25% LP) = 6.5% plus impact, so it is profitable whenever the day's pot (2% of volume) times the attacker's fraction exceeds that, i.e. daily volume above ~3.25x the eligible float value (normal for a launch-day memecoin with only ~12% of supply circulating). Hook fees can also be avoided entirely by entering/exiting through a range-order LP position, since the hook has no liquidity callbacks, making the snipe nearly free. The brief's 'weighted strictly by balance' for holders is defeated by a time-insensitive weight.","line":98,"path":"src/DeezRewards.sol","reproduction":"Eligible float 120M DEEZ worth X; day volume 5X => pot 0.1X. At t=snapshotOf(e)-12 attacker buys 120M-equivalent (cost 0.0325*value each way); at t=snapshotOf(e)+1 sells. Attacker claim(e) = 0.1X*120/240 = 0.05X vs fees ~0.065*X... at volume 10X pot 0.2X, payout 0.1X > cost 0.065X: profit, existing holders' share halved. Expected: weight by time-averaged balance over the epoch; actual: end-second balance only.","severity":"medium","snippet":"        return (epoch + 1) * EPOCH_LENGTH - 1;","title":"Single-second epoch snapshot lets a large trader capture a whole day's pot by holding only across the epoch boundary"}],"hash":"d0eb0ef9f63fb2689b378b63611f70e977ab001ffdbaa7972f5184e70a40677c","nodeId":"a844bbe9-40a8-4586-91f3-05a1aaebe33a","outcome":"completed","summary":"I found two economic defects and wrote them to `.imd-findings.json`. It also has a coverage row for all 13 entry points. The working tree is unchanged: I deleted my scratch test, and git shows nothing modified (the findings file appears to be ignored by git).\n\n1. **High: anyone can claim on behalf of a contract and lock its rewards away for good** (`src/DeezRewards.sol:166`). `claim()` can be called by anyone for any account, and it always pays that account. The README says shares that contract holders never claim go back to holders after the 365-day window. The main case is the swarm's Merkle distributor, which holds 10% of supply. But any third party can call `claim(distributor, epochs)` first. That sends the $IMD to a contract that has no way to move it, and because the share now counts as claimed, `recycle()` can never return it. Up to 10% of every day's fees can be lost this way, plus the share of any other contract holding $DEEZ. I wrote a proof test and ran it: it fails on the current code because 100 $IMD ends up stuck in a stand-in distributor. The proof source is in the finding.\n\n2. **Medium: one-second snapshot lets a trader take a large share of a day's rewards** (`src/DeezRewards.sol:98`). Rewards are weighted by the balance held in the last second of each UTC day, not by balance over time. A trader can buy just before that second and sell just after. The round trip costs about 6.5% (2% hook fee plus the 1.25% pool fee, each way), so this pays whenever daily volume is above roughly 3.25 times the value of the eligible tokens. The finding works through an example that exceeds that. A trader may also be able to avoid most of the 2% fee by entering and leaving through a one-sided liquidity position, because the hook doesn't watch liquidity changes. I did not test either part; the finding is reasoning and arithmetic only.\n\n**Coverage:**\n- **Fee math:** I traced the 2% fee through all four swap modes (buy and sell, exact input and exact output), and it comes out correct in each.\n- **Fee collection and harvest:** collecting fees as PoolManager claims and harvesting them into the rewards contract behave as intended.\n- **Token:** the token has no hidden tax and records balance history on both sides of every transfer.\n- **Liquidity lock:** I only checked `DeezLiquidityLock` at a high level. It has no withdraw path, but I did not check its tick-range maths in detail.\n\nThe rest of the 13 entry points are marked `holds`, except `claim` (finding 1) and `accrue` (finding 2).","treeHash":null,"usage":{"cachedInputTokens":470375,"inputTokens":16,"model":"claude-opus-5-5","outputTokens":9256,"runtime":"claude","turns":10,"wallClockMs":134585}},{"artifacts":[],"attempt":1,"bundleHash":"08ade641f0c8997f630602e2161455146985eb5bda652f4fbdd032ebb5743717","device":"f009de0ce52c7b3a","findings":[{"description":"DeezHook records each fee against the epoch it was traded in and harvest()/harvestEpochs() credit it to that epoch's pot later through DeezRewards.credit, including epochs that have already ended (harvest() always credits the previous epoch). DeezRewards.claim, however, marks claimed[epoch][account] = true (a bool) and pays the share of pot.total as it stands at that moment; claim() does not harvest first. Any $IMD credited to the epoch after a holder claimed it can never be claimed by that holder (AlreadyClaimed). Their portion sits in the pot until recycle() after CLAIM_WINDOW (90 epochs) and is then paid to a later epoch's holders. Holders who claim promptly at the start of a new epoch, before anyone calls harvest(), are the ones penalised, and they lose every fee traded since the last harvest of the previous epoch. Later claimers are unaffected, so the payout depends on when people claim, not only on their balances. Suggested fix: track the amount paid per account per epoch (or the pot.total the account was paid against) and pay the difference on a later claim, or have claim() harvest the requested epochs from the hook before paying.","line":206,"path":"src/DeezRewards.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 {DeezToken} from \"src/DeezToken.sol\";\nimport {DeezRewards} from \"src/DeezRewards.sol\";\n\ncontract ProofIMD is ERC20 {\n    constructor() ERC20(\"IMD\", \"IMD\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\n/// @notice A holder who claims an ended epoch before the hook has harvested all of that epoch's fees never\n/// receives its share of the rest: `DeezHook.harvest()` credits the previous epoch's fees to that epoch through\n/// `DeezRewards.credit`, but `claimed[epoch][account]` is a bool, so the early claimer is refused for good.\n/// This test plays the hook (the only `credit` caller) to keep the proof self-contained.\ncontract LateCreditProof is Test {\n    uint256 constant EPOCH = 1 days;\n    uint256 constant START = 100;\n\n    DeezToken token;\n    ProofIMD imd;\n    DeezRewards rewards;\n    address poolManager = address(0x9001);\n    address alice = address(0xA11CE);\n    address bob = address(0xB0B);\n\n    function setUp() public {\n        vm.warp(START * EPOCH);\n        token = new DeezToken();\n        imd = new ProofIMD();\n        rewards = new DeezRewards(address(token), address(imd), poolManager, address(this));\n        token.transfer(poolManager, 900_000_000 ether);\n        token.transfer(alice, 75_000_000 ether); // 75% of the eligible supply all epoch\n        token.transfer(bob, 25_000_000 ether); // 25%\n    }\n\n    /// @dev What DeezHook._harvest does: transfer the $IMD in, then credit it to the epoch it was traded in.\n    function harvestAsHook(uint256 epoch, uint256 amount) internal {\n        imd.mint(address(rewards), amount);\n        rewards.credit(epoch, amount);\n    }\n\n    function claim(address account, uint256 epoch) internal returns (uint256) {\n        uint256[] memory list = new uint256[](1);\n        list[0] = epoch;\n        vm.prank(account);\n        return rewards.claim(account, list);\n    }\n\n    function test_earlyClaimerReceivesItsShareOfFeesHarvestedLater() public {\n        // Midday harvest: 100 IMD of epoch START's fees reach its pot.\n        vm.warp(START * EPOCH + 12 hours);\n        harvestAsHook(START, 100 ether);\n        // Another 100 IMD of fees are traded later in epoch START and sit in the hook unharvested.\n\n        // First second of the next epoch: alice claims epoch START at once.\n        vm.warp((START + 1) * EPOCH);\n        assertEq(claim(alice, START), 75 ether);\n\n        // Anyone then calls harvest(), which credits the late fees to epoch START, as designed.\n        harvestAsHook(START, 100 ether);\n        assertEq(claim(bob, START), 50 ether, \"bob, claiming after the harvest, gets 25% of all 200\");\n\n        // Alice held 75% of the eligible supply for the whole epoch, so she is owed 75% of the 200 IMD of fees\n        // traded in it. She can never get the second 75: a second claim reverts AlreadyClaimed.\n        uint256[] memory list = new uint256[](1);\n        list[0] = START;\n        vm.prank(alice);\n        try rewards.claim(alice, list) {} catch {}\n        assertEq(imd.balanceOf(alice), 150 ether, \"alice is paid 75% of every fee traded in the epoch\");\n    }\n}","reproduction":"Alice holds 75% and bob 25% of the eligible supply for all of epoch 100. At 12:00 the hook credits 100 IMD to epoch 100; another 100 IMD of epoch-100 fees stay pending in the hook. At the first second of epoch 101 alice claims epoch 100 and receives 75 IMD. harvest() then credits the remaining 100 IMD to epoch 100. Bob claims and gets 50 IMD (25% of 200). Expected: alice receives 150 IMD (75% of the 200 IMD traded in the epoch). Actual: 75 IMD, and a second claim reverts AlreadyClaimed(100, alice). Her 75 IMD stays in the pot until it is recycled to other holders.","severity":"medium","title":"Holder who claims an epoch before its last fees are harvested permanently loses its share of those fees"}],"hash":"d571eca43de6a6261cebe6e76ca73273e730406f579904197bedb346d6fa2218","nodeId":"e910c260-0167-4152-8022-4988fa5969aa","outcome":"completed","summary":"I updated my two test files for the new contracts, and the full suite passes: 148 tests, 0 failures, run with the verifier's fuzz seed. I also found one defect, which I've reported in `.imd-findings.json` instead of writing a test that treats it as correct.\n\n**What changed in the contracts.** After my last round, `DeezRewards` was changed in three ways that broke 14 of my tests:\n- only the holder can claim their own share;\n- rewards are weighted by the average balance held over the whole day-long epoch, not the balance at its last second;\n- fees are credited to the epoch they were traded in.\n\nMy tests had been checking the old behaviour, so I rewrote them. I didn't touch `src/` or anyone else's tests.\n\n**`test/DeezFailurePaths.t.sol`** (distributor section, 20 tests):\n- **Who can claim:** a third party, even the hook, can't claim for someone else. The pool, burn address, hook and rewards contract can't claim even for themselves.\n- **Weighting at the edges:** a wallet that buys in the last second of an epoch weighs 1/86400 of its balance. A transfer in the first second of the next epoch doesn't count. Weight reads and `finalize` revert until the epoch has ended.\n- **Refusals:** an empty batch and a batch with a duplicate epoch both revert, and so does an epoch that hasn't ended yet. A share that rounds to zero is refused rather than paid. `credit` can't be called by anyone but the hook, and can't spend more than it received.\n- **Recycling:** an epoch with no eligible holders pays nobody and its pot comes back through `recycle`. Recycling opens exactly when the 90-epoch claim window ends, and a closed epoch stays closed.\n- **Fuzzing:** with balances moved at random times, all holders together never take more than the pot. Each weight is rounded down slightly, so what's left over is a few wei per unit of pot-to-supply ratio, and it can still be recycled. I set the tolerance to that bound rather than a flat 3 wei.\n\n**`test/DeezRewardsInvariant.t.sol`:**\n- The random-sequence handler now claims as the holder itself.\n- A new handler action has third parties attempt claims, backed by a new invariant that they never succeed.\n- The \"nobody is overpaid\" check now compares against the average-balance weight. It had been failing because it used the old end-of-epoch balance.\n- I checked that claims actually succeed during the runs, so the invariants are testing something.\n\n**The defect (medium severity), in `src/DeezRewards.sol:206`.** Someone who claims an epoch early can lose part of their rewards for good.\n- **How it happens:** `harvest()` credits fees to the epoch they were traded in, including one that has already ended. But a claim records only a yes/no \"claimed\" flag per holder, and claiming doesn't harvest first.\n- **Effect:** if a holder claims right after an epoch ends, before anyone harvests that epoch's remaining fees, they never get their share of those fees. Their share sits unclaimed for 90 epochs and is then recycled to other holders.\n- **Example:** Alice holds 75% of eligible tokens all epoch, and 200 IMD of fees are traded in it. If she claims before the second 100 IMD is harvested, she gets 75 IMD instead of 150, and claiming again fails.\n- **Fix:** either track how much each holder has been paid per epoch and pay the difference later, or have `claim` harvest those epochs first.\n- **Proof:** a self-contained Foundry test is in the findings file. It fails on the current code and will pass once this is fixed.\n\nI couldn't run the protected floor tests here. They need `IMD_HOOK_CREATION_CODE` and other inputs that only the verifier supplies. Since I didn't change any contract code, they should behave as they did in the last round.","treeHash":"434daa4bd27fe6e2369961029a83e3e60abc2e51","usage":{"cachedInputTokens":1425073,"inputTokens":36,"model":"claude-opus-5-5","outputTokens":23231,"runtime":"claude","turns":20,"wallClockMs":602816}}],"verification":[{"checks":[{"durationMs":61642,"exitCode":0,"name":"build","output":"Compiling 95 files with Solc 0.8.26\nSolc 0.8.26 finished in 61.45s\nCompiler run successful!\n","passed":true},{"durationMs":325,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 7 tests for test/DeezToken.t.sol:DeezTokenTest\n[PASS] testFuzz_pastBalanceMatchesBalanceAtThatTime(uint96,uint96,uint32) (runs: 256, μ: 261953, ~: 262323)\n[PASS] test_metadataAndSupply() (gas: 51130)\n[PASS] test_noMintOwnerOrPause() (gas: 146447)\n[PASS] test_pastBalanceRefusesTheCurrentSecondAndTheFuture() (gas: 22382)\n[PASS] test_pastBalancesFollowTransfers() (gas: 452320)\n[PASS] test_transferFromRespectsAllowance() (gas: 309461)\n[PASS] test_transferMovesExactlyWhatItWasAsked() (gas: 125504)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 10.60ms (11.20ms CPU time)\n\nRan 15 tests for test/DeezRewards.t.sol:DeezRewardsTest\n[PASS] testFuzz_claimsNeverExceedThePotAndSplitByBalance(uint96,uint96,uint96) (runs: 256, μ: 770897, ~: 771028)\n[PASS] test_accrueCreditsTheCurrentEpochOnce() (gas: 194843)\n[PASS] test_balanceMovedAfterTheSnapshotDoesNotChangeTheEpoch() (gas: 682970)\n[PASS] test_claimBeforeTheEpochEndsReverts() (gas: 310635)\n[PASS] test_claimManyEpochsAtOnce() (gas: 924958)\n[PASS] test_claimsAreProportionalToBalanceAtTheSnapshot() (gas: 1165994)\n[PASS] test_constructorRefusesZeroAddresses() (gas: 10825)\n[PASS] test_deployerShareIsProportionalToo() (gas: 618699)\n[PASS] test_doubleClaimReverts() (gas: 527454)\n[PASS] test_emptyClaimReverts() (gas: 372098)\n[PASS] test_epochArithmetic() (gas: 22402)\n[PASS] test_excludedAddresses() (gas: 33082)\n[PASS] test_excludedAddressesCannotClaim() (gas: 247798)\n[PASS] test_finalizeRequiresAnEndedEpoch() (gas: 188636)\n[PASS] test_recycleFoldsUnclaimedIntoTheCurrentEpoch() (gas: 864498)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 30.79ms (35.79ms CPU time)\n\nRan 2 tests for test/DeezFlywheel.t.sol:DeezFlywheelTest\n[PASS] test_harvestAndClaimInTheSameEpochSeeTheSamePot() (gas: 1097947)\n[PASS] test_tradesBecomeHolderRewards() (gas: 1824784)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 37.51ms (1.80ms CPU time)\n\nRan 7 tests for test/DeezLiquidityLock.t.sol:DeezLiquidityLockToken1Test\n[PASS] test_lockBothBecomesFullRange() (gas: 1249127)\n[PASS] test_lockDeezOnlyBecomesLiquidityAboveThePrice() (gas: 1157006)\n[PASS] test_lockImdOnlyBecomesABuyWallBelowThePrice() (gas: 819081)\n[PASS] test_lockRefusesBeforeThePoolExists() (gas: 12502753)\n[PASS] test_lockRefusesNothing() (gas: 53455)\n[PASS] test_lockedLiquidityCannotBeRemoved() (gas: 1044837)\n[PASS] test_wiring() (gas: 37388)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 52.72ms (34.06ms CPU time)\n\nRan 21 tests for test/DeezHook.t.sol:DeezHookToken1Test\n[PASS] testFuzz_buyExactInputFeeIsExact(uint96) (runs: 256, μ: 749969, ~: 750107)\n[PASS] testFuzz_sellExactInputFeeIsExact(uint96) (runs: 256, μ: 751185, ~: 751463)\n[PASS] test_buyExactInputTakesTwoPercentOfTheImdPaid() (gas: 783620)\n[PASS] test_buyExactOutputOnAFreshManagerSeededWithTokensOnly() (gas: 757171)\n[PASS] test_buyExactOutputTakesTwoPercentOfTheImdPaid() (gas: 785759)\n[PASS] test_buyOnAFreshManagerSeededWithTokensOnly() (gas: 931337)\n[PASS] test_callbacksRefuseCallersOtherThanThePoolManager() (gas: 122739)\n[PASS] test_constructorRefusesZeroAndSameAddresses() (gas: 8882)\n[PASS] test_constructorWiresTheDistributor() (gas: 53991)\n[PASS] test_feeDeltasSumToZeroForTheHook() (gas: 1349789)\n[PASS] test_feesAccumulateAcrossSwaps() (gas: 932730)\n[PASS] test_harvestMovesFeesToTheDistributorAndCreditsTheEpoch() (gas: 1092459)\n[PASS] test_harvestWithNothingPendingIsANoop() (gas: 38535)\n[PASS] test_hookHasNoOwner() (gas: 54216)\n[PASS] test_initializesFromAnyCallerOnTheLaunchPoolOnce() (gas: 12541767)\n[PASS] test_permissionsMatchTheAddress() (gas: 9044)\n[PASS] test_refusesAPoolWithOtherCurrencies() (gas: 13142627)\n[PASS] test_refusesASecondPool() (gas: 46491)\n[PASS] test_sellExactInputTakesTwoPercentOfTheImdReceived() (gas: 777705)\n[PASS] test_sellExactOutputTakesTwoPercentOfTheImdReceived() (gas: 766821)\n[PASS] test_zeroLiquidityChangeCollectsWithoutHookInterference() (gas: 1080248)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 66.44ms (158.54ms CPU time)\n\nRan 21 tests for test/DeezHook.t.sol:DeezHookToken0Test\n[PASS] testFuzz_buyExactInputFeeIsExact(uint96) (runs: 256, μ: 744481, ~: 744698)\n[PASS] testFuzz_sellExactInputFeeIsExact(uint96) (runs: 256, μ: 756722, ~: 756910)\n[PASS] test_buyExactInputTakesTwoPercentOfTheImdPaid() (gas: 778207)\n[PASS] test_buyExactOutputOnAFreshManagerSeededWithTokensOnly() (gas: 747405)\n[PASS] test_buyExactOutputTakesTwoPercentOfTheImdPaid() (gas: 781650)\n[PASS] test_buyOnAFreshManagerSeededWithTokensOnly() (gas: 920268)\n[PASS] test_callbacksRefuseCallersOtherThanThePoolManager() (gas: 122763)\n[PASS] test_constructorRefusesZeroAndSameAddresses() (gas: 8882)\n[PASS] test_constructorWiresTheDistributor() (gas: 53991)\n[PASS] test_feeDeltasSumToZeroForTheHook() (gas: 1344403)\n[PASS] test_feesAccumulateAcrossSwaps() (gas: 927448)\n[PASS] test_harvestMovesFeesToTheDistributorAndCreditsTheEpoch() (gas: 1087177)\n[PASS] test_harvestWithNothingPendingIsANoop() (gas: 38535)\n[PASS] test_hookHasNoOwner() (gas: 54216)\n[PASS] test_initializesFromAnyCallerOnTheLaunchPoolOnce() (gas: 107542498)\n[PASS] test_permissionsMatchTheAddress() (gas: 9044)\n[PASS] test_refusesAPoolWithOtherCurrencies() (gas: 108208907)\n[PASS] test_refusesASecondPool() (gas: 46503)\n[PASS] test_sellExactInputTakesTwoPercentOfTheImdReceived() (gas: 783148)\n[PASS] test_sellExactOutputTakesTwoPercentOfTheImdReceived() (gas: 770960)\n[PASS] test_zeroLiquidityChangeCollectsWithoutHookInterference() (gas: 1080613)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 114.21ms (251.59ms CPU time)\n\nRan 7 tests for test/DeezLiquidityLock.t.sol:DeezLiquidityLockToken0Test\n[PASS] test_lockBothBecomesFullRange() (gas: 1249091)\n[PASS] test_lockDeezOnlyBecomesLiquidityAboveThePrice() (gas: 1135723)\n[PASS] test_lockImdOnlyBecomesABuyWallBelowThePrice() (gas: 822741)\n[PASS] test_lockRefusesBeforeThePoolExists() (gas: 107502319)\n[PASS] test_lockRefusesNothing() (gas: 53455)\n[PASS] test_lockedLiquidityCannotBeRemoved() (gas: 1044825)\n[PASS] test_wiring() (gas: 37388)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 116.60ms (85.99ms CPU time)\n\nRan 7 test suites in 118.87ms (428.88ms CPU time): 80 tests passed, 0 failed, 0 skipped (80 total tests)\n","passed":true},{"durationMs":108,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"DeezHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"DeezHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"DeezHook.harvest()\",\"DeezHook.unlockCallback(bytes)\",\"DeezLiquidityLock.lock()\",\"DeezLiquidityLock.unlockCallback(bytes)\",\"DeezRewards.accrue()\",\"DeezRewards.claim(address,uint256[])\",\"DeezRewards.finalize(uint256)\",\"DeezRewards.recycle(uint256)\",\"DeezToken.approve(address,uint256)\",\"DeezToken.transfer(address,uint256)\",\"DeezToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"README.md\":208,\"foundry.toml\":20,\"launch.json\":31,\"remappings.txt\":5,\"site/deez.config.js\":21,\"site/index.html\":317,\"site/logo.svg\":10,\"src/DeezHook.sol\":292,\"src/DeezLiquidityLock.sol\":163,\"src/DeezRewards.sol\":200,\"src/DeezToken.sol\":66,\"src/HookFlags.sol\":34,\"test/DeezBase.t.sol\":123,\"test/DeezFlywheel.t.sol\":68,\"test/DeezHook.t.sol\":284,\"test/DeezLiquidityLock.t.sol\":122,\"test/DeezRewards.t.sol\":253,\"test/DeezToken.t.sol\":110,\"test/mocks/MockERC20.sol\":15,\"test/mocks/Swapper.sol\":76},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"15dad5cc14726e959f7b3a6a3b8e4567e76cf899d39cee2d2f229e55830a4df6","verifiedTreeHash":"16f075a68b2d4dfc6c88005c913601085e26801b","verifierVersion":"0.1.0+a6ee6548"},{"checks":[{"durationMs":35465,"exitCode":0,"name":"build","output":"Compiling 91 files with Solc 0.8.26\nSolc 0.8.26 finished in 35.37s\nCompiler run successful!\n","passed":true},{"durationMs":150,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 3 tests for test/DeezFlywheel.t.sol:DeezFlywheelTest\n[PASS] test_feesBelongToTheEpochTheyWereTradedInWheneverHarvested() (gas: 1155995)\n[PASS] test_lastSecondBuyerCannotSnipeTheEpochsPot() (gas: 1637019)\n[PASS] test_tradesBecomeHolderRewards() (gas: 1933397)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 25.20ms (2.82ms CPU time)\n\nRan 21 tests for test/DeezRewards.t.sol:DeezRewardsTest\n[PASS] testFuzz_claimsNeverExceedThePotAndSplitByBalance(uint96,uint96,uint96) (runs: 256, μ: 799567, ~: 799631)\n[PASS] testFuzz_claimsNeverExceedThePotWithMidEpochMoves(uint96,uint32,uint32,uint96) (runs: 256, μ: 1135706, ~: 1135799)\n[PASS] test_accrueCreditsTheCurrentEpochOnce() (gas: 194963)\n[PASS] test_balanceMovedAfterTheEpochDoesNotChangeIt() (gas: 726257)\n[PASS] test_claimBeforeTheEpochEndsReverts() (gas: 311025)\n[PASS] test_claimManyEpochsAtOnce() (gas: 1012203)\n[PASS] test_claimsAreProportionalToTheAverageBalance() (gas: 1241293)\n[PASS] test_constructorRefusesZeroAddresses() (gas: 10847)\n[PASS] test_creditGoesToTheEpochTheHookNames() (gas: 431243)\n[PASS] test_creditRefusesOthersAndUnfundedAmounts() (gas: 221309)\n[PASS] test_creditToAClosedOrFutureEpochGoesToTheCurrentOne() (gas: 405994)\n[PASS] test_deployerShareIsProportionalToo() (gas: 635951)\n[PASS] test_doubleClaimReverts() (gas: 541736)\n[PASS] test_emptyClaimReverts() (gas: 369131)\n[PASS] test_epochArithmetic() (gas: 31377)\n[PASS] test_excludedAddresses() (gas: 33170)\n[PASS] test_excludedAddressesCannotClaim() (gas: 249522)\n[PASS] test_finalizeRequiresAnEndedEpoch() (gas: 195746)\n[PASS] test_onlyTheAccountItselfCanClaim() (gas: 807218)\n[PASS] test_recycleFoldsUnclaimedIntoTheCurrentEpoch() (gas: 887592)\n[PASS] test_rewardsAreWeightedByHoldingTime() (gas: 996604)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 40.74ms (79.37ms CPU time)\n\nRan 11 tests for test/DeezLiquidityLock.t.sol:DeezLiquidityLockToken1Test\n[PASS] test_lockAsFullRangeAndTheLeftoverSingleSided() (gas: 1195357)\n[PASS] test_lockAsRefusesAPriceOutsideTheCallersBounds() (gas: 926515)\n[PASS] test_lockAsRefusesAnEmptySide() (gas: 628626)\n[PASS] test_lockAsSingleSidedIgnoresDustOnTheOtherSide() (gas: 958405)\n[PASS] test_lockBothBecomesFullRange() (gas: 1249163)\n[PASS] test_lockDeezOnlyBecomesLiquidityAboveThePrice() (gas: 1183014)\n[PASS] test_lockImdOnlyBecomesABuyWallBelowThePrice() (gas: 819228)\n[PASS] test_lockRefusesBeforeThePoolExists() (gas: 27725120)\n[PASS] test_lockRefusesNothing() (gas: 53515)\n[PASS] test_lockedLiquidityCannotBeRemoved() (gas: 1044726)\n[PASS] test_wiring() (gas: 37454)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 49.64ms (23.95ms CPU time)\n\nRan 11 tests for test/DeezLiquidityLock.t.sol:DeezLiquidityLockToken0Test\n[PASS] test_lockAsFullRangeAndTheLeftoverSingleSided() (gas: 1191703)\n[PASS] test_lockAsRefusesAPriceOutsideTheCallersBounds() (gas: 931915)\n[PASS] test_lockAsRefusesAnEmptySide() (gas: 628629)\n[PASS] test_lockAsSingleSidedIgnoresDustOnTheOtherSide() (gas: 954747)\n[PASS] test_lockBothBecomesFullRange() (gas: 1249175)\n[PASS] test_lockDeezOnlyBecomesLiquidityAboveThePrice() (gas: 1161767)\n[PASS] test_lockImdOnlyBecomesABuyWallBelowThePrice() (gas: 822866)\n[PASS] test_lockRefusesBeforeThePoolExists() (gas: 57766125)\n[PASS] test_lockRefusesNothing() (gas: 53515)\n[PASS] test_lockedLiquidityCannotBeRemoved() (gas: 1044738)\n[PASS] test_wiring() (gas: 37454)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 49.73ms (43.12ms CPU time)\n\nRan 11 tests for test/DeezToken.t.sol:DeezTokenTest\n[PASS] testFuzz_cumulativeBalanceMatchesHoldingTime(uint96,uint32,uint32) (runs: 256, μ: 283588, ~: 283526)\n[PASS] testFuzz_pastBalanceMatchesBalanceAtThatTime(uint96,uint96,uint32) (runs: 256, μ: 264900, ~: 265144)\n[PASS] test_cumulativeBalanceAllowsTheCurrentSecondButNotTheFuture() (gas: 11964)\n[PASS] test_cumulativeBalanceIntegratesHoldingTime() (gas: 510668)\n[PASS] test_cumulativeBalanceRefusesTheFuture() (gas: 20155)\n[PASS] test_metadataAndSupply() (gas: 51264)\n[PASS] test_noMintOwnerOrPause() (gas: 147107)\n[PASS] test_pastBalanceRefusesTheCurrentSecondAndTheFuture() (gas: 22620)\n[PASS] test_pastBalancesFollowTransfers() (gas: 458770)\n[PASS] test_transferFromRespectsAllowance() (gas: 309071)\n[PASS] test_transferMovesExactlyWhatItWasAsked() (gas: 125405)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 55.63ms (65.83ms CPU time)\n\nRan 28 tests for test/DeezHook.t.sol:DeezHookToken0Test\n[PASS] testFuzz_buyExactInputFeeIsExact(uint96) (runs: 256, μ: 770307, ~: 770527)\n[PASS] testFuzz_sellExactInputFeeIsExact(uint96) (runs: 256, μ: 782431, ~: 782616)\n[PASS] test_buyExactInputTakesTwoPercentOfTheImdPaid() (gas: 804028)\n[PASS] test_buyExactOutputOnAFreshManagerSeededWithTokensOnly() (gas: 773000)\n[PASS] test_buyExactOutputTakesTwoPercentOfTheImdPaid() (gas: 807312)\n[PASS] test_buyOnAFreshManagerSeededWithTokensOnly() (gas: 953247)\n[PASS] test_callbacksRefuseCallersOtherThanThePoolManager() (gas: 122950)\n[PASS] test_constructorRefusesZeroAndSameAddresses() (gas: 9036)\n[PASS] test_constructorWiresTheDistributor() (gas: 54167)\n[PASS] test_deezSpecifiedPartialFillsAreChargedOnWhatActuallyTraded() (gas: 782231)\n[PASS] test_exactInputBuyStoppedByThePriceLimitReverts() (gas: 915662)\n[PASS] test_exactOutputSellBeyondThePoolsImdReverts() (gas: 870500)\n[PASS] test_exactOutputSellStoppedByThePriceLimitReverts() (gas: 628934)\n[PASS] test_exactOutputSellThatFillsOnlyPartOfTheRequestReverts() (gas: 955419)\n[PASS] test_feeDeltasSumToZeroForTheHook() (gas: 1403852)\n[PASS] test_feesAccumulateAcrossSwaps() (gas: 962171)\n[PASS] test_feesAreRecordedAgainstTheEpochTheyWereTradedIn() (gas: 1283991)\n[PASS] test_harvestMovesFeesToTheDistributorAndCreditsTheEpoch() (gas: 1128998)\n[PASS] test_harvestWithNothingPendingIsANoop() (gas: 43121)\n[PASS] test_hookHasNoOwner() (gas: 54392)\n[PASS] test_initializesFromAnyCallerOnTheLaunchPoolOnce() (gas: 57806071)\n[PASS] test_oldEpochsAreHarvestedByNumber() (gas: 1060042)\n[PASS] test_permissionsMatchTheAddress() (gas: 9154)\n[PASS] test_refusesAPoolWithOtherCurrencies() (gas: 58445777)\n[PASS] test_refusesASecondPool() (gas: 46701)\n[PASS] test_sellExactInputTakesTwoPercentOfTheImdReceived() (gas: 808824)\n[PASS] test_sellExactOutputTakesTwoPercentOfTheImdReceived() (gas: 796847)\n[PASS] test_zeroLiquidityChangeCollectsWithoutHookInterference() (gas: 1115173)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 55.56ms (148.75ms CPU time)\n\nRan 28 tests for test/DeezHook.t.sol:DeezHookToken1Test\n[PASS] testFuzz_buyExactInputFeeIsExact(uint96) (runs: 256, μ: 775821, ~: 775987)\n[PASS] testFuzz_sellExactInputFeeIsExact(uint96) (runs: 256, μ: 776915, ~: 777173)\n[PASS] test_buyExactInputTakesTwoPercentOfTheImdPaid() (gas: 809451)\n[PASS] test_buyExactOutputOnAFreshManagerSeededWithTokensOnly() (gas: 782766)\n[PASS] test_buyExactOutputTakesTwoPercentOfTheImdPaid() (gas: 811421)\n[PASS] test_buyOnAFreshManagerSeededWithTokensOnly() (gas: 964326)\n[PASS] test_callbacksRefuseCallersOtherThanThePoolManager() (gas: 122926)\n[PASS] test_constructorRefusesZeroAndSameAddresses() (gas: 9036)\n[PASS] test_constructorWiresTheDistributor() (gas: 54167)\n[PASS] test_deezSpecifiedPartialFillsAreChargedOnWhatActuallyTraded() (gas: 777292)\n[PASS] test_exactInputBuyStoppedByThePriceLimitReverts() (gas: 931494)\n[PASS] test_exactOutputSellBeyondThePoolsImdReverts() (gas: 886466)\n[PASS] test_exactOutputSellStoppedByThePriceLimitReverts() (gas: 624203)\n[PASS] test_exactOutputSellThatFillsOnlyPartOfTheRequestReverts() (gas: 976743)\n[PASS] test_feeDeltasSumToZeroForTheHook() (gas: 1409258)\n[PASS] test_feesAccumulateAcrossSwaps() (gas: 967463)\n[PASS] test_feesAreRecordedAgainstTheEpochTheyWereTradedIn() (gas: 1289283)\n[PASS] test_harvestMovesFeesToTheDistributorAndCreditsTheEpoch() (gas: 1134290)\n[PASS] test_harvestWithNothingPendingIsANoop() (gas: 43121)\n[PASS] test_hookHasNoOwner() (gas: 54392)\n[PASS] test_initializesFromAnyCallerOnTheLaunchPoolOnce() (gas: 27764674)\n[PASS] test_oldEpochsAreHarvestedByNumber() (gas: 1065465)\n[PASS] test_permissionsMatchTheAddress() (gas: 9154)\n[PASS] test_refusesAPoolWithOtherCurrencies() (gas: 28381983)\n[PASS] test_refusesASecondPool() (gas: 46689)\n[PASS] test_sellExactInputTakesTwoPercentOfTheImdReceived() (gas: 803381)\n[PASS] test_sellExactOutputTakesTwoPercentOfTheImdReceived() (gas: 792718)\n[PASS] test_zeroLiquidityChangeCollectsWithoutHookInterference() (gas: 1114818)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 55.60ms (139.24ms CPU time)\n\nRan 7 test suites in 57.46ms (332.10ms CPU time): 113 tests passed, 0 failed, 0 skipped (113 total tests)\n","passed":true},{"durationMs":49,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"DeezHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"DeezHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"DeezHook.harvest()\",\"DeezHook.harvestEpochs(uint256[])\",\"DeezHook.unlockCallback(bytes)\",\"DeezLiquidityLock.lock()\",\"DeezLiquidityLock.lockAs(uint8,int24,int24)\",\"DeezLiquidityLock.unlockCallback(bytes)\",\"DeezRewards.accrue()\",\"DeezRewards.claim(address,uint256[])\",\"DeezRewards.credit(uint256,uint256)\",\"DeezRewards.finalize(uint256)\",\"DeezRewards.recycle(uint256)\",\"DeezToken.approve(address,uint256)\",\"DeezToken.transfer(address,uint256)\",\"DeezToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"README.md\":273,\"foundry.toml\":20,\"remappings.txt\":5,\"site/deez.config.js\":21,\"site/index.html\":317,\"site/logo.svg\":10,\"src/DeezHook.sol\":339,\"src/DeezLiquidityLock.sol\":193,\"src/DeezRewards.sol\":239,\"src/DeezToken.sol\":144,\"src/HookFlags.sol\":34,\"test/DeezBase.t.sol\":123,\"test/DeezFlywheel.t.sol\":106,\"test/DeezHook.t.sol\":387,\"test/DeezLiquidityLock.t.sol\":175,\"test/DeezRewards.t.sol\":386,\"test/DeezToken.t.sol\":174,\"test/mocks/MockERC20.sol\":15,\"test/mocks/Swapper.sol\":85},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":4282,"exitCode":0,"name":"slither","output":"[high/high] uninitialized-state at src/DeezToken.sol:86: DeezToken._history (src/DeezToken.sol#36) is never initialized. It is used in:\n[medium/medium] divide-before-multiply at src/DeezLiquidityLock.sol:183: DeezLiquidityLock._floorToSpacing(int24,int24) (src/DeezLiquidityLock.sol#183-187) performs a multiplication on the result of a division:\n[medium/high] incorrect-equality at src/DeezLiquidityLock.sol:92: DeezLiquidityLock.lockAs(DeezLiquidityLock.Mode,int24,int24) (src/DeezLiquidityLock.sol#92-124) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/DeezLiquidityLock.sol:92: DeezLiquidityLock.lockAs(DeezLiquidityLock.Mode,int24,int24) (src/DeezLiquidityLock.sol#92-124) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/DeezToken.sol:109: DeezToken._checkpoint(address) (src/DeezToken.sol#109-126) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/DeezLiquidityLock.sol:92: DeezLiquidityLock.lockAs(DeezLiquidityLock.Mode,int24,int24) (src/DeezLiquidityLock.sol#92-124) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/DeezLiquidityLock.sol:92: DeezLiquidityLock.lockAs(DeezLiquidityLock.Mode,int24,int24) (src/DeezLiquidityLock.sol#92-124) uses a dangerous strict equality:\n[medium/medium] unused-return at src/DeezLiquidityLock.sol:142: DeezLiquidityLock._settle(Currency,int128) (src/DeezLiquidityLock.sol#142-148) ignores return value by poolManager.settle() (src/DeezLiquidityLock.sol#147)\n[medium/medium] unused-return at src/DeezHook.sol:210: DeezHook._harvest(uint256[]) (src/DeezHook.sol#210-226) ignores return value by poolManager.unlock(abi.encode(amount)) (src/DeezHook.sol#221)\n[medium/medium] unused-return at src/DeezLiquidityLock.sol:127: DeezLiquidityLock.unlockCallback(bytes) (src/DeezLiquidityLock.sol#127-140) ignores return value by (delta,None) = poolManager.modifyLiquidity(key,ModifyLiquidityParams({tickLower:tickLower,tickUpper:tickUpper,liquidityDelta:int256(uint256(liquidity)),salt:bytes32(0)}),) (src/DeezLiquidityLock.sol#130-136)\n[medium/medium] unused-return at src/DeezLiquidityLock.sol:92: DeezLiquidityLock.lockAs(DeezLiquidityLock.Mode,int24,int24) (src/DeezLiquidityLock.sol#92-124) ignores return value by (sqrtPriceX96,tick,None,None) = poolManager.getSlot0(key.toId()) (src/DeezLiquidityLock.sol#101)\n[medium/medium] unused-return at src/DeezHook.sol:210: DeezHook._harvest(uint256[]) (src/DeezHook.sol#210-226) ignores return value by rewards.accrue() (src/DeezHook.sol#220)\n[low/medium] calls-loop at src/DeezRewards.sol:121: DeezRewards.weightOf(address,uint256) (src/DeezRewards.sol#121-125) has external calls inside a loop: token.getCumulativeBalanceOf(account,start + EPOCH_LENGTH) - token.getCumulativeBalanceOf(account,start) (src/DeezRewards.sol#123-124)\n[low/medium] calls-loop at src/DeezRewards.sol:128: DeezRewards.eligibleSupplyAt(uint256) (src/DeezRewards.sol#128-134) has external calls inside a loop: total = token.getCumulativeTotalSupply(start + EPOCH_LENGTH) - token.getCumulativeTotalSupply(start) (src/DeezRewards.sol#130)\n[low/medium] calls-loop at src/DeezRewards.sol:121: DeezRewards.weightOf(address,uint256) (src/DeezRewards.sol#121-125) has external calls inside a loop: token.getCumulativeBalanceOf(account,start + EPOCH_LENGTH) - token.getCumulativeBalanceOf(account,start) (src/DeezRewards.sol#123-124)\n[low/medium] calls-loop at src/DeezHook.sol:210: DeezHook._harvest(uint256[]) (src/DeezHook.sol#210-226) has external calls inside a loop: rewards.credit(epochs[i_scope_0],amounts[i_scope_0]) (src/DeezHook.sol#223)\n[low/medium] calls-loop at src/DeezRewards.sol:128: DeezRewards.eligibleSupplyAt(uint256) (src/DeezRewards.sol#128-134) has external calls inside a loop: total = token.getCumulativeTotalSupply(start + EPOCH_LENGTH) - token.getCumulativeTotalSupply(start) (src/DeezRewards.sol#130)\n[low/medium] calls-loop at src/DeezHook.sol:210: DeezHook._harvest(uint256[]) (src/DeezHook.sol#210-226) has external calls inside a loop: rewards.credit(epochs[i_scope_0],amounts[i_scope_0]) (src/DeezHook.sol#223)\n[low/medium] reentrancy-benign at src/DeezHook.sol:173: Reentrancy in DeezHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/DeezHook.sol#173-191):\n[low/medium] reentrancy-benign at src/DeezLiquidityLock.sol:92: Reentrancy in DeezLiquidityLock.lockAs(DeezLiquidityLock.Mode,int24,int24) (src/DeezLiquidityLock.sol#92-124):\n[low/medium] reentrancy-events at src/DeezLiquidityLock.sol:92: Reentrancy in DeezLiquidityLock.lockAs(DeezLiquidityLock.Mode,int24,int24) (src/DeezLiquidityLock.sol#92-124):\n[low/medium] reentrancy-events at src/DeezHook.sol:210: Reentrancy in DeezHook._harvest(uint256[]) (src/DeezHook.sol#210-226):\n[low/medium] reentrancy-events at src/DeezHook.sol:173: Reentrancy in DeezHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/DeezHook.sol#173-191):\n[low/medium] timestamp at src/DeezToken.sol:56: DeezToken.getPastBalanceOf(address,uint256) (src/DeezToken.sol#56-61) uses timestamp for comparisons\n[low/medium] timestamp at src/DeezRewards.sol:172: DeezRewards.credit(uint256,uint256) (src/DeezRewards.sol#172-180) uses timestamp for comparisons\n[low/medium] timestamp at src/DeezRewards.sol:220: DeezRewards.recycle(uint256) (src/DeezRewards.sol#220-230) uses timestamp for comparisons\n[low/medium] timestamp at src/DeezToken.sol:78: DeezToken.getCumulativeTotalSupply(uint256) (src/DeezToken.sol#78-83) uses timestamp for comparisons\n[low/medium] timestamp at src/DeezToken.sol:68: DeezToken.getCumulativeBalanceOf(address,uint256) (src/DeezToken.sol#68-74) uses timestamp for comparisons\n[low/medium] timestamp at src/DeezRewards.sol:137: DeezRewards.claimable(address,uint256) (src/DeezRewards.sol#137-144) uses timestamp for comparisons\n[low/medium] timestamp at src/DeezToken.sol:109: DeezToken._checkpoint(address) (src/DeezToken.sol#109-126) uses timestamp for comparisons\n[low/medium] timestamp at src/DeezRewards.sol:110: DeezRewards.hasEnded(uint256) (src/DeezRewards.sol#110-112) uses timestamp for comparisons","passed":true},{"durationMs":506,"exitCode":0,"name":"aderyn","output":"[high] reentrancy-state-change at src/DeezHook.sol:184: Reentrancy: State change after external call (9 places)\n[high] unsafe-casting at src/DeezRewards.sol:163: Unsafe Casting of integers (2 places)\n[low] costly-loop at src/DeezHook.sol:212: Costly operations inside loop (2 places)\n[low] internal-function-used-once at src/HookFlags.sol:26: Internal Function Used Only Once\n[low] large-numeric-literal at src/DeezHook.sol:46: Large Numeric Literal (3 places)\n[low] local-variable-shadowing at src/DeezRewards.sol:40: Local Variable Shadows State Variable\n[low] modifier-used-only-once at src/DeezLiquidityLock.sol:62: Modifier Invoked Only Once\n[low] require-revert-in-loop at src/DeezRewards.sol:200: Loop Contains `require`/`revert`\n[low] state-change-without-event at src/DeezHook.sol:132: State Change Without Event\n[low] unchecked-return at src/DeezHook.sol:220: Unchecked Return (4 places)\n[low] unused-import at src/DeezLiquidityLock.sol:9: Unused Import","passed":true},{"durationMs":9575,"exitCode":0,"name":"proof 2f70dfc4600b","output":"Compiling 86 files with Solc 0.8.26\nSolc 0.8.26 finished in 8.96s\nCompiler run successful!\n\nRan 2 tests for test/imd-proof-1fc06378/Proof_2f70dfc4600b.t.sol:FeeOnRequestedAmountTest\n[PASS] test_exactInputBuyPartialFillIsChargedOnTheRequestedInput() (gas: 142236)\n[PASS] test_exactOutputSellThatFillsNothingStillChargesTheFee() (gas: 391306)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 13.43ms (836.48µs CPU time)\n\nRan 1 test suite in 14.04ms (13.43ms CPU time): 2 tests passed, 0 failed, 0 skipped (2 total tests)\n","passed":true},{"durationMs":2428,"exitCode":0,"name":"proof 10fac03ff82f","output":"2026-10-19T11:37:07.667799Z ERROR foundry_compilers_artifacts_solc::sources: error=\"/tmp/imd-verify-AixWzF/repo/test/imd-proof-1fc06378/Proof_2f70dfc4600b.t.sol\": No such file or directory (os error 2)\nCompiling 1 files with Solc 0.8.26\nSolc 0.8.26 finished in 1.93s\nCompiler run successful!\n\nRan 1 test for test/imd-proof-214f0aec/Proof_10fac03ff82f.t.sol:ClaimForContractTest\n[PASS] test_thirdPartyCannotStrandShareOfContractHolder() (gas: 1184967)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 731.87µs (580.64µs CPU time)\n\nRan 1 test suite in 4.68ms (731.87µs CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)\n","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"2be9a13e7c5101e051489bd2efb88b8bd225fa99f8ef0318e1e335203f091358","verifiedTreeHash":"f098df227db06968d7e1267b63e91c87551383b3","verifierVersion":"0.1.0+a6ee6548"},{"checks":[{"durationMs":44232,"exitCode":0,"name":"build","output":"Compiling 98 files with Solc 0.8.26\nSolc 0.8.26 finished in 44.14s\nCompiler run successful!\n","passed":true},{"durationMs":1807,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 7 tests for test/DeezToken.t.sol:DeezTokenTest\n[PASS] testFuzz_pastBalanceMatchesBalanceAtThatTime(uint96,uint96,uint32) (runs: 256, μ: 261953, ~: 262323)\n[PASS] test_metadataAndSupply() (gas: 51130)\n[PASS] test_noMintOwnerOrPause() (gas: 146447)\n[PASS] test_pastBalanceRefusesTheCurrentSecondAndTheFuture() (gas: 22382)\n[PASS] test_pastBalancesFollowTransfers() (gas: 452320)\n[PASS] test_transferFromRespectsAllowance() (gas: 309461)\n[PASS] test_transferMovesExactlyWhatItWasAsked() (gas: 125504)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 9.93ms (10.72ms CPU time)\n\nRan 2 tests for test/DeezFlywheel.t.sol:DeezFlywheelTest\n[PASS] test_harvestAndClaimInTheSameEpochSeeTheSamePot() (gas: 1097947)\n[PASS] test_tradesBecomeHolderRewards() (gas: 1824784)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 19.00ms (1.21ms CPU time)\n\nRan 14 tests for test/DeezFailurePaths.t.sol:DeezRewardsFailurePathsTest\n[PASS] testFuzz_allHoldersTogetherNeverDrainMoreThanThePot(uint96,uint96,uint128,uint32) (runs: 512, μ: 734960, ~: 792609)\n[PASS] test_accrueWithNothingNewCreditsNothing() (gas: 221805)\n[PASS] test_balanceAtTheSnapshotSecondCountsAndTheNextSecondDoesNot() (gas: 622952)\n[PASS] test_batchWithAnUnendedEpochPaysNothing() (gas: 357506)\n[PASS] test_claimPaysTheAccountNotTheCaller() (gas: 327676)\n[PASS] test_closedEpochRefusesClaimsAndASecondRecycle() (gas: 302729)\n[PASS] test_duplicateEpochInOneBatchRevertsTheWholeBatch() (gas: 458048)\n[PASS] test_epochWithNoEligibleSupplyPaysNobodyAndIsRecycled() (gas: 493758)\n[PASS] test_finalizeIsIdempotentAndIgnoresLaterTransfers() (gas: 348698)\n[PASS] test_holderWithNoBalanceAtTheSnapshotHasNothingToClaim() (gas: 307520)\n[PASS] test_recycleOpensExactlyAfterTheClaimWindow() (gas: 406307)\n[PASS] test_recycledRewardsArePaidByTheNewEpochsBalances() (gas: 500437)\n[PASS] test_shareThatRoundsToZeroIsRefusedNotPaid() (gas: 434107)\n[PASS] test_unaccruedDonationGoesToTheCurrentEpochNotAnEndedOne() (gas: 388971)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 29.44ms (32.20ms CPU time)\n\nRan 7 tests for test/DeezLiquidityLock.t.sol:DeezLiquidityLockToken1Test\n[PASS] test_lockBothBecomesFullRange() (gas: 1249127)\n[PASS] test_lockDeezOnlyBecomesLiquidityAboveThePrice() (gas: 1157006)\n[PASS] test_lockImdOnlyBecomesABuyWallBelowThePrice() (gas: 819081)\n[PASS] test_lockRefusesBeforeThePoolExists() (gas: 12502753)\n[PASS] test_lockRefusesNothing() (gas: 53455)\n[PASS] test_lockedLiquidityCannotBeRemoved() (gas: 1044837)\n[PASS] test_wiring() (gas: 37388)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 35.00ms (14.46ms CPU time)\n\nRan 21 tests for test/DeezHook.t.sol:DeezHookToken1Test\n[PASS] testFuzz_buyExactInputFeeIsExact(uint96) (runs: 256, μ: 749969, ~: 750107)\n[PASS] testFuzz_sellExactInputFeeIsExact(uint96) (runs: 256, μ: 751185, ~: 751463)\n[PASS] test_buyExactInputTakesTwoPercentOfTheImdPaid() (gas: 783620)\n[PASS] test_buyExactOutputOnAFreshManagerSeededWithTokensOnly() (gas: 757171)\n[PASS] test_buyExactOutputTakesTwoPercentOfTheImdPaid() (gas: 785759)\n[PASS] test_buyOnAFreshManagerSeededWithTokensOnly() (gas: 931337)\n[PASS] test_callbacksRefuseCallersOtherThanThePoolManager() (gas: 122739)\n[PASS] test_constructorRefusesZeroAndSameAddresses() (gas: 8882)\n[PASS] test_constructorWiresTheDistributor() (gas: 53991)\n[PASS] test_feeDeltasSumToZeroForTheHook() (gas: 1349789)\n[PASS] test_feesAccumulateAcrossSwaps() (gas: 932730)\n[PASS] test_harvestMovesFeesToTheDistributorAndCreditsTheEpoch() (gas: 1092459)\n[PASS] test_harvestWithNothingPendingIsANoop() (gas: 38535)\n[PASS] test_hookHasNoOwner() (gas: 54216)\n[PASS] test_initializesFromAnyCallerOnTheLaunchPoolOnce() (gas: 12541767)\n[PASS] test_permissionsMatchTheAddress() (gas: 9044)\n[PASS] test_refusesAPoolWithOtherCurrencies() (gas: 13142627)\n[PASS] test_refusesASecondPool() (gas: 46491)\n[PASS] test_sellExactInputTakesTwoPercentOfTheImdReceived() (gas: 777705)\n[PASS] test_sellExactOutputTakesTwoPercentOfTheImdReceived() (gas: 766821)\n[PASS] test_zeroLiquidityChangeCollectsWithoutHookInterference() (gas: 1080248)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 50.75ms (87.96ms CPU time)\n\nRan 6 tests for test/DeezFailurePaths.t.sol:DeezHookFailurePathsToken1Test\n[PASS] test_dustSwapsTradeWithoutAFee() (gas: 842197)\n[PASS] test_harvestTwiceMovesTheFeesOnce() (gas: 931906)\n[PASS] test_hookCallbacksRefuseBeforeInitialization() (gas: 12419972)\n[PASS] test_lockSpendsAlmostEverythingAndKeepsItLocked() (gas: 960758)\n[PASS] test_swapCallbacksRefuseAnyOtherPoolKey() (gas: 61886)\n[PASS] test_unlockCallbacksRefuseEveryoneButThePoolManager() (gas: 71498)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 51.26ms (13.17ms CPU time)\n\nRan 15 tests for test/DeezRewards.t.sol:DeezRewardsTest\n[PASS] testFuzz_claimsNeverExceedThePotAndSplitByBalance(uint96,uint96,uint96) (runs: 256, μ: 770897, ~: 771028)\n[PASS] test_accrueCreditsTheCurrentEpochOnce() (gas: 194843)\n[PASS] test_balanceMovedAfterTheSnapshotDoesNotChangeTheEpoch() (gas: 682970)\n[PASS] test_claimBeforeTheEpochEndsReverts() (gas: 310635)\n[PASS] test_claimManyEpochsAtOnce() (gas: 924958)\n[PASS] test_claimsAreProportionalToBalanceAtTheSnapshot() (gas: 1165994)\n[PASS] test_constructorRefusesZeroAddresses() (gas: 10825)\n[PASS] test_deployerShareIsProportionalToo() (gas: 618699)\n[PASS] test_doubleClaimReverts() (gas: 527454)\n[PASS] test_emptyClaimReverts() (gas: 372098)\n[PASS] test_epochArithmetic() (gas: 22402)\n[PASS] test_excludedAddresses() (gas: 33082)\n[PASS] test_excludedAddressesCannotClaim() (gas: 247798)\n[PASS] test_finalizeRequiresAnEndedEpoch() (gas: 188636)\n[PASS] test_recycleFoldsUnclaimedIntoTheCurrentEpoch() (gas: 864498)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 70.19ms (72.50ms CPU time)\n\nRan 21 tests for test/DeezHook.t.sol:DeezHookToken0Test\n[PASS] testFuzz_buyExactInputFeeIsExact(uint96) (runs: 256, μ: 744481, ~: 744698)\n[PASS] testFuzz_sellExactInputFeeIsExact(uint96) (runs: 256, μ: 756722, ~: 756910)\n[PASS] test_buyExactInputTakesTwoPercentOfTheImdPaid() (gas: 778207)\n[PASS] test_buyExactOutputOnAFreshManagerSeededWithTokensOnly() (gas: 747405)\n[PASS] test_buyExactOutputTakesTwoPercentOfTheImdPaid() (gas: 781650)\n[PASS] test_buyOnAFreshManagerSeededWithTokensOnly() (gas: 920268)\n[PASS] test_callbacksRefuseCallersOtherThanThePoolManager() (gas: 122763)\n[PASS] test_constructorRefusesZeroAndSameAddresses() (gas: 8882)\n[PASS] test_constructorWiresTheDistributor() (gas: 53991)\n[PASS] test_feeDeltasSumToZeroForTheHook() (gas: 1344403)\n[PASS] test_feesAccumulateAcrossSwaps() (gas: 927448)\n[PASS] test_harvestMovesFeesToTheDistributorAndCreditsTheEpoch() (gas: 1087177)\n[PASS] test_harvestWithNothingPendingIsANoop() (gas: 38535)\n[PASS] test_hookHasNoOwner() (gas: 54216)\n[PASS] test_initializesFromAnyCallerOnTheLaunchPoolOnce() (gas: 107542498)\n[PASS] test_permissionsMatchTheAddress() (gas: 9044)\n[PASS] test_refusesAPoolWithOtherCurrencies() (gas: 108208907)\n[PASS] test_refusesASecondPool() (gas: 46503)\n[PASS] test_sellExactInputTakesTwoPercentOfTheImdReceived() (gas: 783148)\n[PASS] test_sellExactOutputTakesTwoPercentOfTheImdReceived() (gas: 770960)\n[PASS] test_zeroLiquidityChangeCollectsWithoutHookInterference() (gas: 1080613)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 71.27ms (164.50ms CPU time)\n\nRan 6 tests for test/DeezFailurePaths.t.sol:DeezHookFailurePathsToken0Test\n[PASS] test_dustSwapsTradeWithoutAFee() (gas: 835984)\n[PASS] test_harvestTwiceMovesTheFeesOnce() (gas: 926493)\n[PASS] test_hookCallbacksRefuseBeforeInitialization() (gas: 107426096)\n[PASS] test_lockSpendsAlmostEverythingAndKeepsItLocked() (gas: 960746)\n[PASS] test_swapCallbacksRefuseAnyOtherPoolKey() (gas: 61898)\n[PASS] test_unlockCallbacksRefuseEveryoneButThePoolManager() (gas: 71510)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 78.73ms (40.23ms CPU time)\n\nRan 7 tests for test/DeezLiquidityLock.t.sol:DeezLiquidityLockToken0Test\n[PASS] test_lockBothBecomesFullRange() (gas: 1249091)\n[PASS] test_lockDeezOnlyBecomesLiquidityAboveThePrice() (gas: 1135723)\n[PASS] test_lockImdOnlyBecomesABuyWallBelowThePrice() (gas: 822741)\n[PASS] test_lockRefusesBeforeThePoolExists() (gas: 107502319)\n[PASS] test_lockRefusesNothing() (gas: 53455)\n[PASS] test_lockedLiquidityCannotBeRemoved() (gas: 1044825)\n[PASS] test_wiring() (gas: 37388)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 78.67ms (49.25ms CPU time)\n\nRan 1 test for test/DeezHookInvariant.t.sol:DeezHookInvariantToken1Test\n[PASS]\nDeezHookInvariantToken1Test invariants:\n[PASS] invariant_claimsAreBackedByTheManager\n[PASS] invariant_everyFeeIsPendingOrHarvested\n[PASS] invariant_everyFeeIsTwoPercentOfTheGrossImd\n[PASS] invariant_harvestedImdSitsInTheDistributor\n[PASS] invariant_hookAndDistributorHoldNoDeez\n[PASS] invariant_pendingFeesAreExactlyTheHookClaims\n DeezHookInvariantToken1Test invariants (runs: 64, calls: 2560, reverts: 0)\n\n╭-------------+--------------+-------+---------+----------╮\n| Contract    | Selector     | Calls | Reverts | Discards |\n+=========================================================+\n| HookHandler | buyExactIn   | 371   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | buyExactOut  | 375   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | donate       | 388   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | harvest      | 377   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | sellExactIn  | 340   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | sellExactOut | 348   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | warp         | 361   | 0       | 0        |\n╰-------------+--------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 444.51ms (425.78ms CPU time)\n\nRan 1 test for test/DeezHookInvariant.t.sol:DeezHookInvariantToken0Test\n[PASS]\nDeezHookInvariantToken0Test invariants:\n[PASS] invariant_claimsAreBackedByTheManager\n[PASS] invariant_everyFeeIsPendingOrHarvested\n[PASS] invariant_everyFeeIsTwoPercentOfTheGrossImd\n[PASS] invariant_harvestedImdSitsInTheDistributor\n[PASS] invariant_hookAndDistributorHoldNoDeez\n[PASS] invariant_pendingFeesAreExactlyTheHookClaims\n DeezHookInvariantToken0Test invariants (runs: 64, calls: 2560, reverts: 0)\n\n╭-------------+--------------+-------+---------+----------╮\n| Contract    | Selector     | Calls | Reverts | Discards |\n+=========================================================+\n| HookHandler | buyExactIn   | 371   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | buyExactOut  | 375   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | donate       | 388   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | harvest      | 377   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | sellExactIn  | 340   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | sellExactOut | 348   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | warp         | 361   | 0       | 0        |\n╰-------------+--------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 445.33ms (415.96ms CPU time)\n\nRan 1 test for test/DeezRewardsInvariant.t.sol:DeezRewardsInvariantTest\n[PASS]\nDeezRewardsInvariantTest invariants:\n[PASS] invariant_claimsNeverExceedTheBalanceShare\n[PASS] invariant_closedEpochsNeverReopen\n[PASS] invariant_holdsAtLeastWhatItReserved\n[PASS] invariant_imdIsConserved\n[PASS] invariant_noPotPaysMoreThanItHolds\n[PASS] invariant_reservedEqualsWhatOpenPotsOwe\n[PASS] invariant_supplyNeverChanges\n DeezRewardsInvariantTest invariants (runs: 256, calls: 15360, reverts: 0)\n\n╭----------------+---------------------+-------+---------+----------╮\n| Contract       | Selector            | Calls | Reverts | Discards |\n+===================================================================+\n| RewardsHandler | claim               | 2215  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| RewardsHandler | finalize            | 2164  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| RewardsHandler | fund                | 2095  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| RewardsHandler | recycle             | 2281  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| RewardsHandler | transfer            | 2197  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| RewardsHandler | warp                | 2230  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| RewardsHandler | warpPastClaimWindow | 2178  | 0       | 0        |\n╰----------------+---------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.71s (1.71s CPU time)\n\nRan 13 test suites in 1.71s (3.09s CPU time): 109 tests passed, 0 failed, 0 skipped (109 total tests)\n","passed":true},{"durationMs":52,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"DeezHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"DeezHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"DeezHook.harvest()\",\"DeezHook.unlockCallback(bytes)\",\"DeezLiquidityLock.lock()\",\"DeezLiquidityLock.unlockCallback(bytes)\",\"DeezRewards.accrue()\",\"DeezRewards.claim(address,uint256[])\",\"DeezRewards.finalize(uint256)\",\"DeezRewards.recycle(uint256)\",\"DeezToken.approve(address,uint256)\",\"DeezToken.transfer(address,uint256)\",\"DeezToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"README.md\":208,\"foundry.toml\":20,\"remappings.txt\":5,\"site/deez.config.js\":21,\"site/index.html\":317,\"site/logo.svg\":10,\"src/DeezHook.sol\":292,\"src/DeezLiquidityLock.sol\":163,\"src/DeezRewards.sol\":200,\"src/DeezToken.sol\":66,\"src/HookFlags.sol\":34,\"test/DeezBase.t.sol\":123,\"test/DeezFailurePaths.t.sol\":311,\"test/DeezFlywheel.t.sol\":68,\"test/DeezHook.t.sol\":284,\"test/DeezHookInvariant.t.sol\":153,\"test/DeezLiquidityLock.t.sol\":122,\"test/DeezRewards.t.sol\":253,\"test/DeezRewardsInvariant.t.sol\":227,\"test/DeezToken.t.sol\":110,\"test/mocks/MockERC20.sol\":15,\"test/mocks/Swapper.sol\":76},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"4c99ffd5de5c9b86b3558de43b1162a18d4d8277cfa7b9e8c533f5349a0fd832","verifiedTreeHash":"5a85e02b0d2b90535512278d0ca5290ee0262a82","verifierVersion":"0.1.0+a6ee6548"},{"checks":[{"durationMs":31819,"exitCode":0,"name":"build","output":"Compiling 91 files with Solc 0.8.26\nSolc 0.8.26 finished in 31.71s\nCompiler run successful!\n","passed":true},{"durationMs":148,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 3 tests for test/DeezFlywheel.t.sol:DeezFlywheelTest\n[PASS] test_feesBelongToTheEpochTheyWereTradedInWheneverHarvested() (gas: 1155995)\n[PASS] test_lastSecondBuyerCannotSnipeTheEpochsPot() (gas: 1637019)\n[PASS] test_tradesBecomeHolderRewards() (gas: 1933397)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 21.09ms (3.04ms CPU time)\n\nRan 21 tests for test/DeezRewards.t.sol:DeezRewardsTest\n[PASS] testFuzz_claimsNeverExceedThePotAndSplitByBalance(uint96,uint96,uint96) (runs: 256, μ: 799567, ~: 799631)\n[PASS] testFuzz_claimsNeverExceedThePotWithMidEpochMoves(uint96,uint32,uint32,uint96) (runs: 256, μ: 1135706, ~: 1135799)\n[PASS] test_accrueCreditsTheCurrentEpochOnce() (gas: 194963)\n[PASS] test_balanceMovedAfterTheEpochDoesNotChangeIt() (gas: 726257)\n[PASS] test_claimBeforeTheEpochEndsReverts() (gas: 311025)\n[PASS] test_claimManyEpochsAtOnce() (gas: 1012203)\n[PASS] test_claimsAreProportionalToTheAverageBalance() (gas: 1241293)\n[PASS] test_constructorRefusesZeroAddresses() (gas: 10847)\n[PASS] test_creditGoesToTheEpochTheHookNames() (gas: 431243)\n[PASS] test_creditRefusesOthersAndUnfundedAmounts() (gas: 221309)\n[PASS] test_creditToAClosedOrFutureEpochGoesToTheCurrentOne() (gas: 405994)\n[PASS] test_deployerShareIsProportionalToo() (gas: 635951)\n[PASS] test_doubleClaimReverts() (gas: 541736)\n[PASS] test_emptyClaimReverts() (gas: 369131)\n[PASS] test_epochArithmetic() (gas: 31377)\n[PASS] test_excludedAddresses() (gas: 33170)\n[PASS] test_excludedAddressesCannotClaim() (gas: 249522)\n[PASS] test_finalizeRequiresAnEndedEpoch() (gas: 195746)\n[PASS] test_onlyTheAccountItselfCanClaim() (gas: 807218)\n[PASS] test_recycleFoldsUnclaimedIntoTheCurrentEpoch() (gas: 887592)\n[PASS] test_rewardsAreWeightedByHoldingTime() (gas: 996604)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 39.72ms (79.79ms CPU time)\n\nRan 11 tests for test/DeezToken.t.sol:DeezTokenTest\n[PASS] testFuzz_cumulativeBalanceMatchesHoldingTime(uint96,uint32,uint32) (runs: 256, μ: 283588, ~: 283526)\n[PASS] testFuzz_pastBalanceMatchesBalanceAtThatTime(uint96,uint96,uint32) (runs: 256, μ: 264900, ~: 265144)\n[PASS] test_cumulativeBalanceAllowsTheCurrentSecondButNotTheFuture() (gas: 11964)\n[PASS] test_cumulativeBalanceIntegratesHoldingTime() (gas: 510668)\n[PASS] test_cumulativeBalanceRefusesTheFuture() (gas: 20155)\n[PASS] test_metadataAndSupply() (gas: 51264)\n[PASS] test_noMintOwnerOrPause() (gas: 147107)\n[PASS] test_pastBalanceRefusesTheCurrentSecondAndTheFuture() (gas: 22620)\n[PASS] test_pastBalancesFollowTransfers() (gas: 458770)\n[PASS] test_transferFromRespectsAllowance() (gas: 309071)\n[PASS] test_transferMovesExactlyWhatItWasAsked() (gas: 125405)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 40.84ms (17.07ms CPU time)\n\nRan 11 tests for test/DeezLiquidityLock.t.sol:DeezLiquidityLockToken0Test\n[PASS] test_lockAsFullRangeAndTheLeftoverSingleSided() (gas: 1191703)\n[PASS] test_lockAsRefusesAPriceOutsideTheCallersBounds() (gas: 931915)\n[PASS] test_lockAsRefusesAnEmptySide() (gas: 628629)\n[PASS] test_lockAsSingleSidedIgnoresDustOnTheOtherSide() (gas: 954747)\n[PASS] test_lockBothBecomesFullRange() (gas: 1249175)\n[PASS] test_lockDeezOnlyBecomesLiquidityAboveThePrice() (gas: 1161767)\n[PASS] test_lockImdOnlyBecomesABuyWallBelowThePrice() (gas: 822866)\n[PASS] test_lockRefusesBeforeThePoolExists() (gas: 57766125)\n[PASS] test_lockRefusesNothing() (gas: 53515)\n[PASS] test_lockedLiquidityCannotBeRemoved() (gas: 1044738)\n[PASS] test_wiring() (gas: 37454)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 52.58ms (32.62ms CPU time)\n\nRan 11 tests for test/DeezLiquidityLock.t.sol:DeezLiquidityLockToken1Test\n[PASS] test_lockAsFullRangeAndTheLeftoverSingleSided() (gas: 1195357)\n[PASS] test_lockAsRefusesAPriceOutsideTheCallersBounds() (gas: 926515)\n[PASS] test_lockAsRefusesAnEmptySide() (gas: 628626)\n[PASS] test_lockAsSingleSidedIgnoresDustOnTheOtherSide() (gas: 958405)\n[PASS] test_lockBothBecomesFullRange() (gas: 1249163)\n[PASS] test_lockDeezOnlyBecomesLiquidityAboveThePrice() (gas: 1183014)\n[PASS] test_lockImdOnlyBecomesABuyWallBelowThePrice() (gas: 819228)\n[PASS] test_lockRefusesBeforeThePoolExists() (gas: 27725120)\n[PASS] test_lockRefusesNothing() (gas: 53515)\n[PASS] test_lockedLiquidityCannotBeRemoved() (gas: 1044726)\n[PASS] test_wiring() (gas: 37454)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 53.95ms (35.59ms CPU time)\n\nRan 28 tests for test/DeezHook.t.sol:DeezHookToken1Test\n[PASS] testFuzz_buyExactInputFeeIsExact(uint96) (runs: 256, μ: 775821, ~: 775987)\n[PASS] testFuzz_sellExactInputFeeIsExact(uint96) (runs: 256, μ: 776915, ~: 777173)\n[PASS] test_buyExactInputTakesTwoPercentOfTheImdPaid() (gas: 809451)\n[PASS] test_buyExactOutputOnAFreshManagerSeededWithTokensOnly() (gas: 782766)\n[PASS] test_buyExactOutputTakesTwoPercentOfTheImdPaid() (gas: 811421)\n[PASS] test_buyOnAFreshManagerSeededWithTokensOnly() (gas: 964326)\n[PASS] test_callbacksRefuseCallersOtherThanThePoolManager() (gas: 122926)\n[PASS] test_constructorRefusesZeroAndSameAddresses() (gas: 9036)\n[PASS] test_constructorWiresTheDistributor() (gas: 54167)\n[PASS] test_deezSpecifiedPartialFillsAreChargedOnWhatActuallyTraded() (gas: 777292)\n[PASS] test_exactInputBuyStoppedByThePriceLimitReverts() (gas: 931494)\n[PASS] test_exactOutputSellBeyondThePoolsImdReverts() (gas: 886466)\n[PASS] test_exactOutputSellStoppedByThePriceLimitReverts() (gas: 624203)\n[PASS] test_exactOutputSellThatFillsOnlyPartOfTheRequestReverts() (gas: 976743)\n[PASS] test_feeDeltasSumToZeroForTheHook() (gas: 1409258)\n[PASS] test_feesAccumulateAcrossSwaps() (gas: 967463)\n[PASS] test_feesAreRecordedAgainstTheEpochTheyWereTradedIn() (gas: 1289283)\n[PASS] test_harvestMovesFeesToTheDistributorAndCreditsTheEpoch() (gas: 1134290)\n[PASS] test_harvestWithNothingPendingIsANoop() (gas: 43121)\n[PASS] test_hookHasNoOwner() (gas: 54392)\n[PASS] test_initializesFromAnyCallerOnTheLaunchPoolOnce() (gas: 27764674)\n[PASS] test_oldEpochsAreHarvestedByNumber() (gas: 1065465)\n[PASS] test_permissionsMatchTheAddress() (gas: 9154)\n[PASS] test_refusesAPoolWithOtherCurrencies() (gas: 28381983)\n[PASS] test_refusesASecondPool() (gas: 46689)\n[PASS] test_sellExactInputTakesTwoPercentOfTheImdReceived() (gas: 803381)\n[PASS] test_sellExactOutputTakesTwoPercentOfTheImdReceived() (gas: 792718)\n[PASS] test_zeroLiquidityChangeCollectsWithoutHookInterference() (gas: 1114818)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 54.07ms (114.87ms CPU time)\n\nRan 28 tests for test/DeezHook.t.sol:DeezHookToken0Test\n[PASS] testFuzz_buyExactInputFeeIsExact(uint96) (runs: 256, μ: 770307, ~: 770527)\n[PASS] testFuzz_sellExactInputFeeIsExact(uint96) (runs: 256, μ: 782431, ~: 782616)\n[PASS] test_buyExactInputTakesTwoPercentOfTheImdPaid() (gas: 804028)\n[PASS] test_buyExactOutputOnAFreshManagerSeededWithTokensOnly() (gas: 773000)\n[PASS] test_buyExactOutputTakesTwoPercentOfTheImdPaid() (gas: 807312)\n[PASS] test_buyOnAFreshManagerSeededWithTokensOnly() (gas: 953247)\n[PASS] test_callbacksRefuseCallersOtherThanThePoolManager() (gas: 122950)\n[PASS] test_constructorRefusesZeroAndSameAddresses() (gas: 9036)\n[PASS] test_constructorWiresTheDistributor() (gas: 54167)\n[PASS] test_deezSpecifiedPartialFillsAreChargedOnWhatActuallyTraded() (gas: 782231)\n[PASS] test_exactInputBuyStoppedByThePriceLimitReverts() (gas: 915662)\n[PASS] test_exactOutputSellBeyondThePoolsImdReverts() (gas: 870500)\n[PASS] test_exactOutputSellStoppedByThePriceLimitReverts() (gas: 628934)\n[PASS] test_exactOutputSellThatFillsOnlyPartOfTheRequestReverts() (gas: 955419)\n[PASS] test_feeDeltasSumToZeroForTheHook() (gas: 1403852)\n[PASS] test_feesAccumulateAcrossSwaps() (gas: 962171)\n[PASS] test_feesAreRecordedAgainstTheEpochTheyWereTradedIn() (gas: 1283991)\n[PASS] test_harvestMovesFeesToTheDistributorAndCreditsTheEpoch() (gas: 1128998)\n[PASS] test_harvestWithNothingPendingIsANoop() (gas: 43121)\n[PASS] test_hookHasNoOwner() (gas: 54392)\n[PASS] test_initializesFromAnyCallerOnTheLaunchPoolOnce() (gas: 57806071)\n[PASS] test_oldEpochsAreHarvestedByNumber() (gas: 1060042)\n[PASS] test_permissionsMatchTheAddress() (gas: 9154)\n[PASS] test_refusesAPoolWithOtherCurrencies() (gas: 58445777)\n[PASS] test_refusesASecondPool() (gas: 46701)\n[PASS] test_sellExactInputTakesTwoPercentOfTheImdReceived() (gas: 808824)\n[PASS] test_sellExactOutputTakesTwoPercentOfTheImdReceived() (gas: 796847)\n[PASS] test_zeroLiquidityChangeCollectsWithoutHookInterference() (gas: 1115173)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 54.11ms (140.63ms CPU time)\n\nRan 7 test suites in 54.84ms (316.35ms CPU time): 113 tests passed, 0 failed, 0 skipped (113 total tests)\n","passed":true},{"durationMs":51,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"DeezHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"DeezHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"DeezHook.harvest()\",\"DeezHook.harvestEpochs(uint256[])\",\"DeezHook.unlockCallback(bytes)\",\"DeezLiquidityLock.lock()\",\"DeezLiquidityLock.lockAs(uint8,int24,int24)\",\"DeezLiquidityLock.unlockCallback(bytes)\",\"DeezRewards.accrue()\",\"DeezRewards.claim(address,uint256[])\",\"DeezRewards.credit(uint256,uint256)\",\"DeezRewards.finalize(uint256)\",\"DeezRewards.recycle(uint256)\",\"DeezToken.approve(address,uint256)\",\"DeezToken.transfer(address,uint256)\",\"DeezToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"README.md\":273,\"foundry.toml\":20,\"launch.json\":31,\"remappings.txt\":5,\"site/deez.config.js\":21,\"site/index.html\":317,\"site/logo.svg\":10,\"src/DeezHook.sol\":339,\"src/DeezLiquidityLock.sol\":193,\"src/DeezRewards.sol\":239,\"src/DeezToken.sol\":144,\"src/HookFlags.sol\":34,\"test/DeezBase.t.sol\":123,\"test/DeezFlywheel.t.sol\":106,\"test/DeezHook.t.sol\":387,\"test/DeezLiquidityLock.t.sol\":175,\"test/DeezRewards.t.sol\":386,\"test/DeezToken.t.sol\":174,\"test/mocks/MockERC20.sol\":15,\"test/mocks/Swapper.sol\":85},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"6ee3f5142d3b76497897138a1b045c55dc3436e7146c1990fc60b706c8aecc3b","verifiedTreeHash":"c74900a2aa128cd91a5d8e02f62a588c4fc435c5","verifierVersion":"0.1.0+a6ee6548"},{"checks":[{"durationMs":66339,"exitCode":0,"name":"build","output":"Compiling 95 files with Solc 0.8.26\nSolc 0.8.26 finished in 66.12s\nCompiler run successful!\n","passed":true},{"durationMs":379,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 7 tests for test/DeezToken.t.sol:DeezTokenTest\n[PASS] testFuzz_pastBalanceMatchesBalanceAtThatTime(uint96,uint96,uint32) (runs: 256, μ: 261953, ~: 262323)\n[PASS] test_metadataAndSupply() (gas: 51130)\n[PASS] test_noMintOwnerOrPause() (gas: 146447)\n[PASS] test_pastBalanceRefusesTheCurrentSecondAndTheFuture() (gas: 22382)\n[PASS] test_pastBalancesFollowTransfers() (gas: 452320)\n[PASS] test_transferFromRespectsAllowance() (gas: 309461)\n[PASS] test_transferMovesExactlyWhatItWasAsked() (gas: 125504)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 15.80ms (16.30ms CPU time)\n\nRan 15 tests for test/DeezRewards.t.sol:DeezRewardsTest\n[PASS] testFuzz_claimsNeverExceedThePotAndSplitByBalance(uint96,uint96,uint96) (runs: 256, μ: 770897, ~: 771028)\n[PASS] test_accrueCreditsTheCurrentEpochOnce() (gas: 194843)\n[PASS] test_balanceMovedAfterTheSnapshotDoesNotChangeTheEpoch() (gas: 682970)\n[PASS] test_claimBeforeTheEpochEndsReverts() (gas: 310635)\n[PASS] test_claimManyEpochsAtOnce() (gas: 924958)\n[PASS] test_claimsAreProportionalToBalanceAtTheSnapshot() (gas: 1165994)\n[PASS] test_constructorRefusesZeroAddresses() (gas: 10825)\n[PASS] test_deployerShareIsProportionalToo() (gas: 618699)\n[PASS] test_doubleClaimReverts() (gas: 527454)\n[PASS] test_emptyClaimReverts() (gas: 372098)\n[PASS] test_epochArithmetic() (gas: 22402)\n[PASS] test_excludedAddresses() (gas: 33082)\n[PASS] test_excludedAddressesCannotClaim() (gas: 247798)\n[PASS] test_finalizeRequiresAnEndedEpoch() (gas: 188636)\n[PASS] test_recycleFoldsUnclaimedIntoTheCurrentEpoch() (gas: 864498)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 44.42ms (47.19ms CPU time)\n\nRan 2 tests for test/DeezFlywheel.t.sol:DeezFlywheelTest\n[PASS] test_harvestAndClaimInTheSameEpochSeeTheSamePot() (gas: 1097947)\n[PASS] test_tradesBecomeHolderRewards() (gas: 1824784)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 49.63ms (2.47ms CPU time)\n\nRan 7 tests for test/DeezLiquidityLock.t.sol:DeezLiquidityLockToken1Test\n[PASS] test_lockBothBecomesFullRange() (gas: 1249127)\n[PASS] test_lockDeezOnlyBecomesLiquidityAboveThePrice() (gas: 1157006)\n[PASS] test_lockImdOnlyBecomesABuyWallBelowThePrice() (gas: 819081)\n[PASS] test_lockRefusesBeforeThePoolExists() (gas: 12502753)\n[PASS] test_lockRefusesNothing() (gas: 53455)\n[PASS] test_lockedLiquidityCannotBeRemoved() (gas: 1044837)\n[PASS] test_wiring() (gas: 37388)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 50.66ms (27.74ms CPU time)\n\nRan 21 tests for test/DeezHook.t.sol:DeezHookToken1Test\n[PASS] testFuzz_buyExactInputFeeIsExact(uint96) (runs: 256, μ: 749969, ~: 750107)\n[PASS] testFuzz_sellExactInputFeeIsExact(uint96) (runs: 256, μ: 751185, ~: 751463)\n[PASS] test_buyExactInputTakesTwoPercentOfTheImdPaid() (gas: 783620)\n[PASS] test_buyExactOutputOnAFreshManagerSeededWithTokensOnly() (gas: 757171)\n[PASS] test_buyExactOutputTakesTwoPercentOfTheImdPaid() (gas: 785759)\n[PASS] test_buyOnAFreshManagerSeededWithTokensOnly() (gas: 931337)\n[PASS] test_callbacksRefuseCallersOtherThanThePoolManager() (gas: 122739)\n[PASS] test_constructorRefusesZeroAndSameAddresses() (gas: 8882)\n[PASS] test_constructorWiresTheDistributor() (gas: 53991)\n[PASS] test_feeDeltasSumToZeroForTheHook() (gas: 1349789)\n[PASS] test_feesAccumulateAcrossSwaps() (gas: 932730)\n[PASS] test_harvestMovesFeesToTheDistributorAndCreditsTheEpoch() (gas: 1092459)\n[PASS] test_harvestWithNothingPendingIsANoop() (gas: 38535)\n[PASS] test_hookHasNoOwner() (gas: 54216)\n[PASS] test_initializesFromAnyCallerOnTheLaunchPoolOnce() (gas: 12541767)\n[PASS] test_permissionsMatchTheAddress() (gas: 9044)\n[PASS] test_refusesAPoolWithOtherCurrencies() (gas: 13142627)\n[PASS] test_refusesASecondPool() (gas: 46491)\n[PASS] test_sellExactInputTakesTwoPercentOfTheImdReceived() (gas: 777705)\n[PASS] test_sellExactOutputTakesTwoPercentOfTheImdReceived() (gas: 766821)\n[PASS] test_zeroLiquidityChangeCollectsWithoutHookInterference() (gas: 1080248)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 111.72ms (164.20ms CPU time)\n\nRan 7 tests for test/DeezLiquidityLock.t.sol:DeezLiquidityLockToken0Test\n[PASS] test_lockBothBecomesFullRange() (gas: 1249091)\n[PASS] test_lockDeezOnlyBecomesLiquidityAboveThePrice() (gas: 1135723)\n[PASS] test_lockImdOnlyBecomesABuyWallBelowThePrice() (gas: 822741)\n[PASS] test_lockRefusesBeforeThePoolExists() (gas: 107502319)\n[PASS] test_lockRefusesNothing() (gas: 53455)\n[PASS] test_lockedLiquidityCannotBeRemoved() (gas: 1044825)\n[PASS] test_wiring() (gas: 37388)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 151.60ms (89.67ms CPU time)\n\nRan 21 tests for test/DeezHook.t.sol:DeezHookToken0Test\n[PASS] testFuzz_buyExactInputFeeIsExact(uint96) (runs: 256, μ: 744481, ~: 744698)\n[PASS] testFuzz_sellExactInputFeeIsExact(uint96) (runs: 256, μ: 756722, ~: 756910)\n[PASS] test_buyExactInputTakesTwoPercentOfTheImdPaid() (gas: 778207)\n[PASS] test_buyExactOutputOnAFreshManagerSeededWithTokensOnly() (gas: 747405)\n[PASS] test_buyExactOutputTakesTwoPercentOfTheImdPaid() (gas: 781650)\n[PASS] test_buyOnAFreshManagerSeededWithTokensOnly() (gas: 920268)\n[PASS] test_callbacksRefuseCallersOtherThanThePoolManager() (gas: 122763)\n[PASS] test_constructorRefusesZeroAndSameAddresses() (gas: 8882)\n[PASS] test_constructorWiresTheDistributor() (gas: 53991)\n[PASS] test_feeDeltasSumToZeroForTheHook() (gas: 1344403)\n[PASS] test_feesAccumulateAcrossSwaps() (gas: 927448)\n[PASS] test_harvestMovesFeesToTheDistributorAndCreditsTheEpoch() (gas: 1087177)\n[PASS] test_harvestWithNothingPendingIsANoop() (gas: 38535)\n[PASS] test_hookHasNoOwner() (gas: 54216)\n[PASS] test_initializesFromAnyCallerOnTheLaunchPoolOnce() (gas: 107542498)\n[PASS] test_permissionsMatchTheAddress() (gas: 9044)\n[PASS] test_refusesAPoolWithOtherCurrencies() (gas: 108208907)\n[PASS] test_refusesASecondPool() (gas: 46503)\n[PASS] test_sellExactInputTakesTwoPercentOfTheImdReceived() (gas: 783148)\n[PASS] test_sellExactOutputTakesTwoPercentOfTheImdReceived() (gas: 770960)\n[PASS] test_zeroLiquidityChangeCollectsWithoutHookInterference() (gas: 1080613)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 152.82ms (292.89ms CPU time)\n\nRan 7 test suites in 157.13ms (576.66ms CPU time): 80 tests passed, 0 failed, 0 skipped (80 total tests)\n","passed":true},{"durationMs":163,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"DeezHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"DeezHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"DeezHook.harvest()\",\"DeezHook.unlockCallback(bytes)\",\"DeezLiquidityLock.lock()\",\"DeezLiquidityLock.unlockCallback(bytes)\",\"DeezRewards.accrue()\",\"DeezRewards.claim(address,uint256[])\",\"DeezRewards.finalize(uint256)\",\"DeezRewards.recycle(uint256)\",\"DeezToken.approve(address,uint256)\",\"DeezToken.transfer(address,uint256)\",\"DeezToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"README.md\":208,\"foundry.toml\":20,\"remappings.txt\":5,\"site/deez.config.js\":21,\"site/index.html\":317,\"site/logo.svg\":10,\"src/DeezHook.sol\":292,\"src/DeezLiquidityLock.sol\":163,\"src/DeezRewards.sol\":200,\"src/DeezToken.sol\":66,\"src/HookFlags.sol\":34,\"test/DeezBase.t.sol\":123,\"test/DeezFlywheel.t.sol\":68,\"test/DeezHook.t.sol\":284,\"test/DeezLiquidityLock.t.sol\":122,\"test/DeezRewards.t.sol\":253,\"test/DeezToken.t.sol\":110,\"test/mocks/MockERC20.sol\":15,\"test/mocks/Swapper.sol\":76},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":9390,"exitCode":0,"name":"slither","output":"[medium/medium] divide-before-multiply at src/DeezLiquidityLock.sol:153: DeezLiquidityLock._floorToSpacing(int24,int24) (src/DeezLiquidityLock.sol#153-157) performs a multiplication on the result of a division:\n[medium/high] incorrect-equality at src/DeezLiquidityLock.sol:65: DeezLiquidityLock.lock() (src/DeezLiquidityLock.sol#65-94) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/DeezLiquidityLock.sol:65: DeezLiquidityLock.lock() (src/DeezLiquidityLock.sol#65-94) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/DeezLiquidityLock.sol:65: DeezLiquidityLock.lock() (src/DeezLiquidityLock.sol#65-94) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/DeezLiquidityLock.sol:65: DeezLiquidityLock.lock() (src/DeezLiquidityLock.sol#65-94) uses a dangerous strict equality:\n[medium/medium] unused-return at src/DeezToken.sol:60: DeezToken._update(address,address,uint256) (src/DeezToken.sol#60-65) ignores return value by _history[from].push(now_,uint208(balanceOf(from))) (src/DeezToken.sol#63)\n[medium/medium] unused-return at src/DeezHook.sol:180: DeezHook.harvest() (src/DeezHook.sol#180-185) ignores return value by rewards.accrue() (src/DeezHook.sol#184)\n[medium/medium] unused-return at src/DeezHook.sol:180: DeezHook.harvest() (src/DeezHook.sol#180-185) ignores return value by poolManager.unlock() (src/DeezHook.sol#183)\n[medium/medium] unused-return at src/DeezLiquidityLock.sol:112: DeezLiquidityLock._settle(Currency,int128) (src/DeezLiquidityLock.sol#112-118) ignores return value by poolManager.settle() (src/DeezLiquidityLock.sol#117)\n[medium/medium] unused-return at src/DeezLiquidityLock.sol:97: DeezLiquidityLock.unlockCallback(bytes) (src/DeezLiquidityLock.sol#97-110) ignores return value by (delta,None) = poolManager.modifyLiquidity(key,ModifyLiquidityParams({tickLower:tickLower,tickUpper:tickUpper,liquidityDelta:int256(uint256(liquidity)),salt:bytes32(0)}),) (src/DeezLiquidityLock.sol#100-106)\n[medium/medium] unused-return at src/DeezLiquidityLock.sol:65: DeezLiquidityLock.lock() (src/DeezLiquidityLock.sol#65-94) ignores return value by (sqrtPriceX96,tick,None,None) = poolManager.getSlot0(key.toId()) (src/DeezLiquidityLock.sol#72)\n[medium/medium] unused-return at src/DeezToken.sol:60: DeezToken._update(address,address,uint256) (src/DeezToken.sol#60-65) ignores return value by _history[to].push(now_,uint208(balanceOf(to))) (src/DeezToken.sol#64)\n[low/medium] calls-loop at src/DeezRewards.sol:113: DeezRewards.eligibleSupplyAt(uint256) (src/DeezRewards.sol#113-119) has external calls inside a loop: excluded = token.getPastBalanceOf(poolManager,snapshot) + token.getPastBalanceOf(BURN_ADDRESS,snapshot) + token.getPastBalanceOf(hook,snapshot) + token.getPastBalanceOf(address(this),snapshot) (src/DeezRewards.sol#115-117)\n[low/medium] calls-loop at src/DeezRewards.sol:166: DeezRewards.claim(address,uint256[]) (src/DeezRewards.sol#166-186) has external calls inside a loop: amount = uint256(pot.total) * token.getPastBalanceOf(account,snapshotOf(epoch)) / supply (src/DeezRewards.sol#177)\n[low/medium] calls-loop at src/DeezRewards.sol:122: DeezRewards.claimable(address,uint256) (src/DeezRewards.sol#122-129) has external calls inside a loop: uint256(pot.total) * token.getPastBalanceOf(account,snapshotOf(epoch)) / supply (src/DeezRewards.sol#128)\n[low/medium] calls-loop at src/DeezRewards.sol:113: DeezRewards.eligibleSupplyAt(uint256) (src/DeezRewards.sol#113-119) has external calls inside a loop: excluded = token.getPastBalanceOf(poolManager,snapshot) + token.getPastBalanceOf(BURN_ADDRESS,snapshot) + token.getPastBalanceOf(hook,snapshot) + token.getPastBalanceOf(address(this),snapshot) (src/DeezRewards.sol#115-117)\n[low/medium] calls-loop at src/DeezRewards.sol:113: DeezRewards.eligibleSupplyAt(uint256) (src/DeezRewards.sol#113-119) has external calls inside a loop: token.totalSupply() - excluded (src/DeezRewards.sol#118)\n[low/medium] calls-loop at src/DeezRewards.sol:113: DeezRewards.eligibleSupplyAt(uint256) (src/DeezRewards.sol#113-119) has external calls inside a loop: token.totalSupply() - excluded (src/DeezRewards.sol#118)\n[low/medium] reentrancy-benign at src/DeezHook.sol:161: Reentrancy in DeezHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/DeezHook.sol#161-176):\n[low/medium] reentrancy-benign at src/DeezLiquidityLock.sol:65: Reentrancy in DeezLiquidityLock.lock() (src/DeezLiquidityLock.sol#65-94):\n[low/medium] reentrancy-events at src/DeezHook.sol:188: Reentrancy in DeezHook.unlockCallback(bytes) (src/DeezHook.sol#188-196):\n[low/medium] reentrancy-events at src/DeezLiquidityLock.sol:65: Reentrancy in DeezLiquidityLock.lock() (src/DeezLiquidityLock.sol#65-94):\n[low/medium] reentrancy-events at src/DeezHook.sol:161: Reentrancy in DeezHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/DeezHook.sol#161-176):\n[low/medium] timestamp at src/DeezRewards.sol:189: DeezRewards.recycle(uint256) (src/DeezRewards.sol#189-199) uses timestamp for comparisons\n[low/medium] timestamp at src/DeezRewards.sol:102: DeezRewards.hasEnded(uint256) (src/DeezRewards.sol#102-104) uses timestamp for comparisons\n[low/medium] timestamp at src/DeezRewards.sol:122: DeezRewards.claimable(address,uint256) (src/DeezRewards.sol#122-129) uses timestamp for comparisons\n[low/medium] timestamp at src/DeezToken.sol:43: DeezToken.getPastBalanceOf(address,uint256) (src/DeezToken.sol#43-47) uses timestamp for comparisons","passed":true},{"durationMs":1167,"exitCode":0,"name":"aderyn","output":"[high] reentrancy-state-change at src/DeezHook.sol:171: Reentrancy: State change after external call (8 places)\n[high] unsafe-casting at src/DeezRewards.sol:148: Unsafe Casting of integers\n[low] costly-loop at src/DeezRewards.sol:169: Costly operations inside loop\n[low] internal-function-used-once at src/HookFlags.sol:26: Internal Function Used Only Once\n[low] large-numeric-literal at src/DeezHook.sol:42: Large Numeric Literal (3 places)\n[low] local-variable-shadowing at src/DeezRewards.sol:36: Local Variable Shadows State Variable\n[low] modifier-used-only-once at src/DeezLiquidityLock.sol:52: Modifier Invoked Only Once\n[low] require-revert-in-loop at src/DeezRewards.sol:169: Loop Contains `require`/`revert`\n[low] state-change-without-event at src/DeezHook.sol:124: State Change Without Event\n[low] unchecked-return at src/DeezHook.sol:183: Unchecked Return (4 places)\n[low] unused-import at src/DeezLiquidityLock.sol:9: Unused Import","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"802ff802c001e6a15b56463b7e947d847ee9be7cce43caaeb48f0891470291af","verifiedTreeHash":"75c928605600b603cd41c46ebb2c2b558d0ab88d","verifierVersion":"0.1.0+a6ee6548"},{"checks":[{"durationMs":63723,"exitCode":0,"name":"build","output":"Compiling 95 files with Solc 0.8.26\nSolc 0.8.26 finished in 63.62s\nCompiler run successful!\n","passed":true},{"durationMs":3942,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 4 tests for test/DeezFlywheel.t.sol:DeezFlywheelTest\n[PASS] test_earlyClaimerGetsItsShareOfFeesHarvestedLater() (gas: 2161108)\n[PASS] test_feesBelongToTheEpochTheyWereTradedInWheneverHarvested() (gas: 1164796)\n[PASS] test_lastSecondBuyerCannotSnipeTheEpochsPot() (gas: 1651147)\n[PASS] test_tradesBecomeHolderRewards() (gas: 1947513)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 12.44ms (3.49ms CPU time)\n\nRan 11 tests for test/DeezLiquidityLock.t.sol:DeezLiquidityLockToken0Test\n[PASS] test_lockAsFullRangeAndTheLeftoverSingleSided() (gas: 1191703)\n[PASS] test_lockAsRefusesAPriceOutsideTheCallersBounds() (gas: 931915)\n[PASS] test_lockAsRefusesAnEmptySide() (gas: 628629)\n[PASS] test_lockAsSingleSidedIgnoresDustOnTheOtherSide() (gas: 954747)\n[PASS] test_lockBothBecomesFullRange() (gas: 1249175)\n[PASS] test_lockDeezOnlyBecomesLiquidityAboveThePrice() (gas: 1161767)\n[PASS] test_lockImdOnlyBecomesABuyWallBelowThePrice() (gas: 822866)\n[PASS] test_lockRefusesBeforeThePoolExists() (gas: 20366189)\n[PASS] test_lockRefusesNothing() (gas: 53515)\n[PASS] test_lockedLiquidityCannotBeRemoved() (gas: 1044738)\n[PASS] test_wiring() (gas: 37454)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 33.61ms (23.24ms CPU time)\n\nRan 7 tests for test/DeezFailurePaths.t.sol:DeezHookFailurePathsToken0Test\n[PASS] test_claimThatRevertsLeavesTheHooksFeesUntouched() (gas: 1905565)\n[PASS] test_dustSwapsTradeWithoutAFee() (gas: 840546)\n[PASS] test_harvestTwiceMovesTheFeesOnce() (gas: 963997)\n[PASS] test_hookCallbacksRefuseBeforeInitialization() (gas: 20284510)\n[PASS] test_lockSpendsAlmostEverythingAndKeepsItLocked() (gas: 960567)\n[PASS] test_swapCallbacksRefuseAnyOtherPoolKey() (gas: 61951)\n[PASS] test_unlockCallbacksRefuseEveryoneButThePoolManager() (gas: 71556)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 38.33ms (24.51ms CPU time)\n\nRan 11 tests for test/DeezToken.t.sol:DeezTokenTest\n[PASS] testFuzz_cumulativeBalanceMatchesHoldingTime(uint96,uint32,uint32) (runs: 256, μ: 283588, ~: 283526)\n[PASS] testFuzz_pastBalanceMatchesBalanceAtThatTime(uint96,uint96,uint32) (runs: 256, μ: 264900, ~: 265144)\n[PASS] test_cumulativeBalanceAllowsTheCurrentSecondButNotTheFuture() (gas: 11964)\n[PASS] test_cumulativeBalanceIntegratesHoldingTime() (gas: 510668)\n[PASS] test_cumulativeBalanceRefusesTheFuture() (gas: 20155)\n[PASS] test_metadataAndSupply() (gas: 51264)\n[PASS] test_noMintOwnerOrPause() (gas: 147107)\n[PASS] test_pastBalanceRefusesTheCurrentSecondAndTheFuture() (gas: 22620)\n[PASS] test_pastBalancesFollowTransfers() (gas: 458770)\n[PASS] test_transferFromRespectsAllowance() (gas: 309071)\n[PASS] test_transferMovesExactlyWhatItWasAsked() (gas: 125405)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 74.26ms (85.29ms CPU time)\n\nRan 7 tests for test/DeezFailurePaths.t.sol:DeezHookFailurePathsToken1Test\n[PASS] test_claimThatRevertsLeavesTheHooksFeesUntouched() (gas: 1911241)\n[PASS] test_dustSwapsTradeWithoutAFee() (gas: 846759)\n[PASS] test_harvestTwiceMovesTheFeesOnce() (gas: 969420)\n[PASS] test_hookCallbacksRefuseBeforeInitialization() (gas: 192282674)\n[PASS] test_lockSpendsAlmostEverythingAndKeepsItLocked() (gas: 960555)\n[PASS] test_swapCallbacksRefuseAnyOtherPoolKey() (gas: 61939)\n[PASS] test_unlockCallbacksRefuseEveryoneButThePoolManager() (gas: 71544)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 90.82ms (68.50ms CPU time)\n\nRan 28 tests for test/DeezHook.t.sol:DeezHookToken0Test\n[PASS] testFuzz_buyExactInputFeeIsExact(uint96) (runs: 256, μ: 770294, ~: 770523)\n[PASS] testFuzz_sellExactInputFeeIsExact(uint96) (runs: 256, μ: 782419, ~: 782584)\n[PASS] test_buyExactInputTakesTwoPercentOfTheImdPaid() (gas: 804028)\n[PASS] test_buyExactOutputOnAFreshManagerSeededWithTokensOnly() (gas: 773000)\n[PASS] test_buyExactOutputTakesTwoPercentOfTheImdPaid() (gas: 807312)\n[PASS] test_buyOnAFreshManagerSeededWithTokensOnly() (gas: 953247)\n[PASS] test_callbacksRefuseCallersOtherThanThePoolManager() (gas: 122950)\n[PASS] test_constructorRefusesZeroAndSameAddresses() (gas: 9036)\n[PASS] test_constructorWiresTheDistributor() (gas: 54167)\n[PASS] test_deezSpecifiedPartialFillsAreChargedOnWhatActuallyTraded() (gas: 782231)\n[PASS] test_exactInputBuyStoppedByThePriceLimitReverts() (gas: 915662)\n[PASS] test_exactOutputSellBeyondThePoolsImdReverts() (gas: 870500)\n[PASS] test_exactOutputSellStoppedByThePriceLimitReverts() (gas: 628934)\n[PASS] test_exactOutputSellThatFillsOnlyPartOfTheRequestReverts() (gas: 955419)\n[PASS] test_feeDeltasSumToZeroForTheHook() (gas: 1403852)\n[PASS] test_feesAccumulateAcrossSwaps() (gas: 962171)\n[PASS] test_feesAreRecordedAgainstTheEpochTheyWereTradedIn() (gas: 1283991)\n[PASS] test_harvestMovesFeesToTheDistributorAndCreditsTheEpoch() (gas: 1128998)\n[PASS] test_harvestWithNothingPendingIsANoop() (gas: 43121)\n[PASS] test_hookHasNoOwner() (gas: 54392)\n[PASS] test_initializesFromAnyCallerOnTheLaunchPoolOnce() (gas: 20405597)\n[PASS] test_oldEpochsAreHarvestedByNumber() (gas: 1060042)\n[PASS] test_permissionsMatchTheAddress() (gas: 9154)\n[PASS] test_refusesAPoolWithOtherCurrencies() (gas: 21015718)\n[PASS] test_refusesASecondPool() (gas: 46701)\n[PASS] test_sellExactInputTakesTwoPercentOfTheImdReceived() (gas: 808824)\n[PASS] test_sellExactOutputTakesTwoPercentOfTheImdReceived() (gas: 796847)\n[PASS] test_zeroLiquidityChangeCollectsWithoutHookInterference() (gas: 1115173)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 92.73ms (170.79ms CPU time)\n\nRan 23 tests for test/DeezFailurePaths.t.sol:DeezRewardsFailurePathsTest\n[PASS] testFuzz_allHoldersTogetherNeverDrainMoreThanThePot(uint96,uint96,uint128,uint32,uint32) (runs: 512, μ: 922506, ~: 988235)\n[PASS] testFuzz_claimOrderAndLateFeesDoNotChangeFinalShares(uint96,uint128,uint128,uint8,uint32) (runs: 512, μ: 1141526, ~: 1102273)\n[PASS] test_accrueWithNothingNewCreditsNothing() (gas: 222212)\n[PASS] test_balanceMovedInTheFirstSecondOfTheNextEpochDoesNotCount() (gas: 567537)\n[PASS] test_batchWithAnUnendedEpochPaysNothing() (gas: 424942)\n[PASS] test_closedEpochRefusesClaimsAndASecondRecycle() (gas: 328962)\n[PASS] test_creditCannotBeForgedOrOverdrawn() (gas: 209779)\n[PASS] test_duplicateEpochInOneBatchPaysItOnce() (gas: 479568)\n[PASS] test_emptyBatchReverts() (gas: 224005)\n[PASS] test_epochWithNoEligibleSupplyPaysNobodyAndIsRecycled() (gas: 632781)\n[PASS] test_excludedAddressesCannotClaimEvenForThemselves() (gas: 323557)\n[PASS] test_finalizeIsIdempotentAndIgnoresLaterTransfers() (gas: 392352)\n[PASS] test_holderWithNoBalanceInTheEpochHasNothingToClaim() (gas: 342141)\n[PASS] test_lastSecondBuyerWeighsOneSecond() (gas: 763033)\n[PASS] test_lateFeeAfterSomeClaimedStillSplitsExactlyByBalance() (gas: 715790)\n[PASS] test_nobodyCanClaimForSomeoneElse() (gas: 441727)\n[PASS] test_pendingFeesOfAClosedEpochGoToTheCurrentPot() (gas: 510506)\n[PASS] test_recycleOfAnEpochThatNeverHadAPotClosesItForNothing() (gas: 99934)\n[PASS] test_recycleOpensExactlyAfterTheClaimWindow() (gas: 447688)\n[PASS] test_recycledRewardsArePaidByTheNewEpochsBalances() (gas: 551247)\n[PASS] test_shareThatRoundsToZeroIsRefusedNotPaid() (gas: 472382)\n[PASS] test_unaccruedDonationGoesToTheCurrentEpochNotAnEndedOne() (gas: 430201)\n[PASS] test_weightReadsRevertUntilTheEpochHasEnded() (gas: 222967)\nSuite result: ok. 23 passed; 0 failed; 0 skipped; finished in 92.84ms (174.93ms CPU time)\n\nRan 11 tests for test/DeezLiquidityLock.t.sol:DeezLiquidityLockToken1Test\n[PASS] test_lockAsFullRangeAndTheLeftoverSingleSided() (gas: 1195357)\n[PASS] test_lockAsRefusesAPriceOutsideTheCallersBounds() (gas: 926515)\n[PASS] test_lockAsRefusesAnEmptySide() (gas: 628626)\n[PASS] test_lockAsSingleSidedIgnoresDustOnTheOtherSide() (gas: 958405)\n[PASS] test_lockBothBecomesFullRange() (gas: 1249163)\n[PASS] test_lockDeezOnlyBecomesLiquidityAboveThePrice() (gas: 1183014)\n[PASS] test_lockImdOnlyBecomesABuyWallBelowThePrice() (gas: 819228)\n[PASS] test_lockRefusesBeforeThePoolExists() (gas: 192355329)\n[PASS] test_lockRefusesNothing() (gas: 53515)\n[PASS] test_lockedLiquidityCannotBeRemoved() (gas: 1044726)\n[PASS] test_wiring() (gas: 37454)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 109.85ms (63.13ms CPU time)\n\nRan 28 tests for test/DeezHook.t.sol:DeezHookToken1Test\n[PASS] testFuzz_buyExactInputFeeIsExact(uint96) (runs: 256, μ: 775810, ~: 775946)\n[PASS] testFuzz_sellExactInputFeeIsExact(uint96) (runs: 256, μ: 776898, ~: 777169)\n[PASS] test_buyExactInputTakesTwoPercentOfTheImdPaid() (gas: 809451)\n[PASS] test_buyExactOutputOnAFreshManagerSeededWithTokensOnly() (gas: 782766)\n[PASS] test_buyExactOutputTakesTwoPercentOfTheImdPaid() (gas: 811421)\n[PASS] test_buyOnAFreshManagerSeededWithTokensOnly() (gas: 964326)\n[PASS] test_callbacksRefuseCallersOtherThanThePoolManager() (gas: 122926)\n[PASS] test_constructorRefusesZeroAndSameAddresses() (gas: 9036)\n[PASS] test_constructorWiresTheDistributor() (gas: 54167)\n[PASS] test_deezSpecifiedPartialFillsAreChargedOnWhatActuallyTraded() (gas: 777292)\n[PASS] test_exactInputBuyStoppedByThePriceLimitReverts() (gas: 931494)\n[PASS] test_exactOutputSellBeyondThePoolsImdReverts() (gas: 886466)\n[PASS] test_exactOutputSellStoppedByThePriceLimitReverts() (gas: 624203)\n[PASS] test_exactOutputSellThatFillsOnlyPartOfTheRequestReverts() (gas: 976743)\n[PASS] test_feeDeltasSumToZeroForTheHook() (gas: 1409258)\n[PASS] test_feesAccumulateAcrossSwaps() (gas: 967463)\n[PASS] test_feesAreRecordedAgainstTheEpochTheyWereTradedIn() (gas: 1289283)\n[PASS] test_harvestMovesFeesToTheDistributorAndCreditsTheEpoch() (gas: 1134290)\n[PASS] test_harvestWithNothingPendingIsANoop() (gas: 43121)\n[PASS] test_hookHasNoOwner() (gas: 54392)\n[PASS] test_initializesFromAnyCallerOnTheLaunchPoolOnce() (gas: 192396393)\n[PASS] test_oldEpochsAreHarvestedByNumber() (gas: 1065465)\n[PASS] test_permissionsMatchTheAddress() (gas: 9154)\n[PASS] test_refusesAPoolWithOtherCurrencies() (gas: 193096784)\n[PASS] test_refusesASecondPool() (gas: 46689)\n[PASS] test_sellExactInputTakesTwoPercentOfTheImdReceived() (gas: 803381)\n[PASS] test_sellExactOutputTakesTwoPercentOfTheImdReceived() (gas: 792718)\n[PASS] test_zeroLiquidityChangeCollectsWithoutHookInterference() (gas: 1114818)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 112.31ms (262.49ms CPU time)\n\nRan 24 tests for test/DeezRewards.t.sol:DeezRewardsTest\n[PASS] testFuzz_claimsNeverExceedThePotAndSplitByBalance(uint96,uint96,uint96) (runs: 256, μ: 838100, ~: 838238)\n[PASS] testFuzz_claimsNeverExceedThePotWithMidEpochMoves(uint96,uint32,uint32,uint96) (runs: 256, μ: 1195709, ~: 1195802)\n[PASS] test_accrueCreditsTheCurrentEpochOnce() (gas: 195007)\n[PASS] test_balanceMovedAfterTheEpochDoesNotChangeIt() (gas: 746713)\n[PASS] test_claimBeforeTheEpochEndsReverts() (gas: 336533)\n[PASS] test_claimHarvestsOnlyTheEpochsItPays() (gas: 617087)\n[PASS] test_claimHarvestsThePendingFeesOfTheEpochFirst() (gas: 972560)\n[PASS] test_claimManyEpochsAtOnce() (gas: 1055389)\n[PASS] test_claimsAreProportionalToTheAverageBalance() (gas: 1288158)\n[PASS] test_constructorRefusesZeroAddresses() (gas: 10913)\n[PASS] test_creditGoesToTheEpochTheHookNames() (gas: 431309)\n[PASS] test_creditRefusesOthersAndUnfundedAmounts() (gas: 221353)\n[PASS] test_creditToAClosedOrFutureEpochGoesToTheCurrentOne() (gas: 406015)\n[PASS] test_deployerShareIsProportionalToo() (gas: 674502)\n[PASS] test_doubleClaimPaysNothingMore() (gas: 820604)\n[PASS] test_emptyClaimReverts() (gas: 381656)\n[PASS] test_epochArithmetic() (gas: 31377)\n[PASS] test_excludedAddresses() (gas: 33214)\n[PASS] test_excludedAddressesCannotClaim() (gas: 249694)\n[PASS] test_finalizeRequiresAnEndedEpoch() (gas: 195790)\n[PASS] test_lateCreditIsPaidOnASecondClaim() (gas: 1056780)\n[PASS] test_onlyTheAccountItselfCanClaim() (gas: 840182)\n[PASS] test_recycleFoldsUnclaimedIntoTheCurrentEpoch() (gas: 934426)\n[PASS] test_rewardsAreWeightedByHoldingTime() (gas: 1035184)\nSuite result: ok. 24 passed; 0 failed; 0 skipped; finished in 113.84ms (163.91ms CPU time)\n\nRan 1 test for test/DeezHookInvariant.t.sol:DeezHookInvariantToken0Test\n[PASS]\nDeezHookInvariantToken0Test invariants:\n[PASS] invariant_claimsAreBackedByTheManager\n[PASS] invariant_everyFeeIsPendingOrHarvested\n[PASS] invariant_everyFeeIsTwoPercentOfTheGrossImd\n[PASS] invariant_harvestedImdSitsInTheDistributor\n[PASS] invariant_hookAndDistributorHoldNoDeez\n[PASS] invariant_pendingFeesAreExactlyTheHookClaims\n DeezHookInvariantToken0Test invariants (runs: 64, calls: 2560, reverts: 0)\n\n╭-------------+--------------+-------+---------+----------╮\n| Contract    | Selector     | Calls | Reverts | Discards |\n+=========================================================+\n| HookHandler | buyExactIn   | 371   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | buyExactOut  | 375   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | donate       | 388   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | harvest      | 377   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | sellExactIn  | 340   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | sellExactOut | 348   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | warp         | 361   | 0       | 0        |\n╰-------------+--------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 456.92ms (443.73ms CPU time)\n\nRan 1 test for test/DeezHookInvariant.t.sol:DeezHookInvariantToken1Test\n[PASS]\nDeezHookInvariantToken1Test invariants:\n[PASS] invariant_claimsAreBackedByTheManager\n[PASS] invariant_everyFeeIsPendingOrHarvested\n[PASS] invariant_everyFeeIsTwoPercentOfTheGrossImd\n[PASS] invariant_harvestedImdSitsInTheDistributor\n[PASS] invariant_hookAndDistributorHoldNoDeez\n[PASS] invariant_pendingFeesAreExactlyTheHookClaims\n DeezHookInvariantToken1Test invariants (runs: 64, calls: 2560, reverts: 0)\n\n╭-------------+--------------+-------+---------+----------╮\n| Contract    | Selector     | Calls | Reverts | Discards |\n+=========================================================+\n| HookHandler | buyExactIn   | 371   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | buyExactOut  | 375   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | donate       | 388   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | harvest      | 377   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | sellExactIn  | 340   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | sellExactOut | 348   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | warp         | 361   | 0       | 0        |\n╰-------------+--------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 502.55ms (454.63ms CPU time)\n\nRan 1 test for test/DeezRewardsInvariant.t.sol:DeezRewardsInvariantTest\n[PASS]\nDeezRewardsInvariantTest invariants:\n[PASS] invariant_claimsNeverExceedTheBalanceShare\n[PASS] invariant_closedEpochsNeverReopen\n[PASS] invariant_holdsAtLeastWhatItReserved\n[PASS] invariant_imdIsConserved\n[PASS] invariant_noHolderIsPaidMoreThanItsShareOfAnyPot\n[PASS] invariant_noPotPaysMoreThanItHolds\n[PASS] invariant_onlyTheAccountClaimsItsShare\n[PASS] invariant_pendingFeesAreTrackedExactly\n[PASS] invariant_reservedEqualsWhatOpenPotsOwe\n[PASS] invariant_supplyNeverChanges\n DeezRewardsInvariantTest invariants (runs: 256, calls: 15360, reverts: 0)\n\n╭----------------+---------------------+-------+---------+----------╮\n| Contract       | Selector            | Calls | Reverts | Discards |\n+===================================================================+\n| RewardsHandler | claim               | 1535  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| RewardsHandler | claimFor            | 1576  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| RewardsHandler | finalize            | 1544  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| RewardsHandler | fund                | 1570  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| RewardsHandler | harvest             | 1500  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| RewardsHandler | lateFee             | 1521  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| RewardsHandler | recycle             | 1584  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| RewardsHandler | transfer            | 1496  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| RewardsHandler | warp                | 1561  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| RewardsHandler | warpPastClaimWindow | 1473  | 0       | 0        |\n╰----------------+---------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.83s (3.83s CPU time)\n\nRan 13 test suites in 3.84s (5.56s CPU time): 157 tests passed, 0 failed, 0 skipped (157 total tests)\n","passed":true},{"durationMs":59,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"DeezHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"DeezHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"DeezHook.harvest()\",\"DeezHook.harvestEpochs(uint256[])\",\"DeezHook.unlockCallback(bytes)\",\"DeezLiquidityLock.lock()\",\"DeezLiquidityLock.lockAs(uint8,int24,int24)\",\"DeezLiquidityLock.unlockCallback(bytes)\",\"DeezRewards.accrue()\",\"DeezRewards.claim(address,uint256[])\",\"DeezRewards.credit(uint256,uint256)\",\"DeezRewards.finalize(uint256)\",\"DeezRewards.recycle(uint256)\",\"DeezToken.approve(address,uint256)\",\"DeezToken.transfer(address,uint256)\",\"DeezToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"README.md\":299,\"foundry.toml\":20,\"remappings.txt\":5,\"site/deez.config.js\":21,\"site/index.html\":317,\"site/logo.svg\":10,\"src/DeezHook.sol\":341,\"src/DeezLiquidityLock.sol\":193,\"src/DeezRewards.sol\":262,\"src/DeezToken.sol\":144,\"src/HookFlags.sol\":34,\"test/DeezBase.t.sol\":123,\"test/DeezFailurePaths.t.sol\":527,\"test/DeezFlywheel.t.sol\":150,\"test/DeezHook.t.sol\":387,\"test/DeezHookInvariant.t.sol\":153,\"test/DeezLiquidityLock.t.sol\":175,\"test/DeezRewards.t.sol\":448,\"test/DeezRewardsInvariant.t.sol\":304,\"test/DeezToken.t.sol\":174,\"test/mocks/MockERC20.sol\":15,\"test/mocks/MockHook.sol\":36,\"test/mocks/Swapper.sol\":85},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"a2417343e24aba98ee546ffd06d1aa65456c15c70dbb56ecd3a64642679ad318","verifiedTreeHash":"7db7d7a307a5437aa122b77298d056f637e77526","verifierVersion":"0.1.0+a6ee6548"},{"checks":[{"durationMs":36858,"exitCode":0,"name":"build","output":"Compiling 92 files with Solc 0.8.26\nSolc 0.8.26 finished in 36.76s\nCompiler run successful!\n","passed":true},{"durationMs":219,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 11 tests for test/DeezToken.t.sol:DeezTokenTest\n[PASS] testFuzz_cumulativeBalanceMatchesHoldingTime(uint96,uint32,uint32) (runs: 256, μ: 283589, ~: 283526)\n[PASS] testFuzz_pastBalanceMatchesBalanceAtThatTime(uint96,uint96,uint32) (runs: 256, μ: 264902, ~: 265144)\n[PASS] test_cumulativeBalanceAllowsTheCurrentSecondButNotTheFuture() (gas: 11964)\n[PASS] test_cumulativeBalanceIntegratesHoldingTime() (gas: 510668)\n[PASS] test_cumulativeBalanceRefusesTheFuture() (gas: 20155)\n[PASS] test_metadataAndSupply() (gas: 51264)\n[PASS] test_noMintOwnerOrPause() (gas: 147107)\n[PASS] test_pastBalanceRefusesTheCurrentSecondAndTheFuture() (gas: 22620)\n[PASS] test_pastBalancesFollowTransfers() (gas: 458770)\n[PASS] test_transferFromRespectsAllowance() (gas: 309071)\n[PASS] test_transferMovesExactlyWhatItWasAsked() (gas: 125405)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 8.17ms (16.93ms CPU time)\n\nRan 24 tests for test/DeezRewards.t.sol:DeezRewardsTest\n[PASS] testFuzz_claimsNeverExceedThePotAndSplitByBalance(uint96,uint96,uint96) (runs: 256, μ: 838100, ~: 838238)\n[PASS] testFuzz_claimsNeverExceedThePotWithMidEpochMoves(uint96,uint32,uint32,uint96) (runs: 256, μ: 1195692, ~: 1195787)\n[PASS] test_accrueCreditsTheCurrentEpochOnce() (gas: 195007)\n[PASS] test_balanceMovedAfterTheEpochDoesNotChangeIt() (gas: 746713)\n[PASS] test_claimBeforeTheEpochEndsReverts() (gas: 336533)\n[PASS] test_claimHarvestsOnlyTheEpochsItPays() (gas: 617087)\n[PASS] test_claimHarvestsThePendingFeesOfTheEpochFirst() (gas: 972560)\n[PASS] test_claimManyEpochsAtOnce() (gas: 1055389)\n[PASS] test_claimsAreProportionalToTheAverageBalance() (gas: 1288158)\n[PASS] test_constructorRefusesZeroAddresses() (gas: 10913)\n[PASS] test_creditGoesToTheEpochTheHookNames() (gas: 431309)\n[PASS] test_creditRefusesOthersAndUnfundedAmounts() (gas: 221353)\n[PASS] test_creditToAClosedOrFutureEpochGoesToTheCurrentOne() (gas: 406015)\n[PASS] test_deployerShareIsProportionalToo() (gas: 674502)\n[PASS] test_doubleClaimPaysNothingMore() (gas: 820604)\n[PASS] test_emptyClaimReverts() (gas: 381656)\n[PASS] test_epochArithmetic() (gas: 31377)\n[PASS] test_excludedAddresses() (gas: 33214)\n[PASS] test_excludedAddressesCannotClaim() (gas: 249694)\n[PASS] test_finalizeRequiresAnEndedEpoch() (gas: 195790)\n[PASS] test_lateCreditIsPaidOnASecondClaim() (gas: 1056780)\n[PASS] test_onlyTheAccountItselfCanClaim() (gas: 840182)\n[PASS] test_recycleFoldsUnclaimedIntoTheCurrentEpoch() (gas: 934426)\n[PASS] test_rewardsAreWeightedByHoldingTime() (gas: 1035184)\nSuite result: ok. 24 passed; 0 failed; 0 skipped; finished in 49.03ms (96.34ms CPU time)\n\nRan 11 tests for test/DeezLiquidityLock.t.sol:DeezLiquidityLockToken0Test\n[PASS] test_lockAsFullRangeAndTheLeftoverSingleSided() (gas: 1191703)\n[PASS] test_lockAsRefusesAPriceOutsideTheCallersBounds() (gas: 931915)\n[PASS] test_lockAsRefusesAnEmptySide() (gas: 628629)\n[PASS] test_lockAsSingleSidedIgnoresDustOnTheOtherSide() (gas: 954747)\n[PASS] test_lockBothBecomesFullRange() (gas: 1249175)\n[PASS] test_lockDeezOnlyBecomesLiquidityAboveThePrice() (gas: 1161767)\n[PASS] test_lockImdOnlyBecomesABuyWallBelowThePrice() (gas: 822866)\n[PASS] test_lockRefusesBeforeThePoolExists() (gas: 20366189)\n[PASS] test_lockRefusesNothing() (gas: 53515)\n[PASS] test_lockedLiquidityCannotBeRemoved() (gas: 1044738)\n[PASS] test_wiring() (gas: 37454)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 95.10ms (32.55ms CPU time)\n\nRan 4 tests for test/DeezFlywheel.t.sol:DeezFlywheelTest\n[PASS] test_earlyClaimerGetsItsShareOfFeesHarvestedLater() (gas: 2161108)\n[PASS] test_feesBelongToTheEpochTheyWereTradedInWheneverHarvested() (gas: 1164796)\n[PASS] test_lastSecondBuyerCannotSnipeTheEpochsPot() (gas: 1651147)\n[PASS] test_tradesBecomeHolderRewards() (gas: 1947513)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 95.20ms (3.55ms CPU time)\n\nRan 11 tests for test/DeezLiquidityLock.t.sol:DeezLiquidityLockToken1Test\n[PASS] test_lockAsFullRangeAndTheLeftoverSingleSided() (gas: 1195357)\n[PASS] test_lockAsRefusesAPriceOutsideTheCallersBounds() (gas: 926515)\n[PASS] test_lockAsRefusesAnEmptySide() (gas: 628626)\n[PASS] test_lockAsSingleSidedIgnoresDustOnTheOtherSide() (gas: 958405)\n[PASS] test_lockBothBecomesFullRange() (gas: 1249163)\n[PASS] test_lockDeezOnlyBecomesLiquidityAboveThePrice() (gas: 1183014)\n[PASS] test_lockImdOnlyBecomesABuyWallBelowThePrice() (gas: 819228)\n[PASS] test_lockRefusesBeforeThePoolExists() (gas: 192355329)\n[PASS] test_lockRefusesNothing() (gas: 53515)\n[PASS] test_lockedLiquidityCannotBeRemoved() (gas: 1044726)\n[PASS] test_wiring() (gas: 37454)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 105.32ms (76.19ms CPU time)\n\nRan 28 tests for test/DeezHook.t.sol:DeezHookToken1Test\n[PASS] testFuzz_buyExactInputFeeIsExact(uint96) (runs: 256, μ: 775810, ~: 775946)\n[PASS] testFuzz_sellExactInputFeeIsExact(uint96) (runs: 256, μ: 776898, ~: 777169)\n[PASS] test_buyExactInputTakesTwoPercentOfTheImdPaid() (gas: 809451)\n[PASS] test_buyExactOutputOnAFreshManagerSeededWithTokensOnly() (gas: 782766)\n[PASS] test_buyExactOutputTakesTwoPercentOfTheImdPaid() (gas: 811421)\n[PASS] test_buyOnAFreshManagerSeededWithTokensOnly() (gas: 964326)\n[PASS] test_callbacksRefuseCallersOtherThanThePoolManager() (gas: 122926)\n[PASS] test_constructorRefusesZeroAndSameAddresses() (gas: 9036)\n[PASS] test_constructorWiresTheDistributor() (gas: 54167)\n[PASS] test_deezSpecifiedPartialFillsAreChargedOnWhatActuallyTraded() (gas: 777292)\n[PASS] test_exactInputBuyStoppedByThePriceLimitReverts() (gas: 931494)\n[PASS] test_exactOutputSellBeyondThePoolsImdReverts() (gas: 886466)\n[PASS] test_exactOutputSellStoppedByThePriceLimitReverts() (gas: 624203)\n[PASS] test_exactOutputSellThatFillsOnlyPartOfTheRequestReverts() (gas: 976743)\n[PASS] test_feeDeltasSumToZeroForTheHook() (gas: 1409258)\n[PASS] test_feesAccumulateAcrossSwaps() (gas: 967463)\n[PASS] test_feesAreRecordedAgainstTheEpochTheyWereTradedIn() (gas: 1289283)\n[PASS] test_harvestMovesFeesToTheDistributorAndCreditsTheEpoch() (gas: 1134290)\n[PASS] test_harvestWithNothingPendingIsANoop() (gas: 43121)\n[PASS] test_hookHasNoOwner() (gas: 54392)\n[PASS] test_initializesFromAnyCallerOnTheLaunchPoolOnce() (gas: 192396393)\n[PASS] test_oldEpochsAreHarvestedByNumber() (gas: 1065465)\n[PASS] test_permissionsMatchTheAddress() (gas: 9154)\n[PASS] test_refusesAPoolWithOtherCurrencies() (gas: 193096784)\n[PASS] test_refusesASecondPool() (gas: 46689)\n[PASS] test_sellExactInputTakesTwoPercentOfTheImdReceived() (gas: 803381)\n[PASS] test_sellExactOutputTakesTwoPercentOfTheImdReceived() (gas: 792718)\n[PASS] test_zeroLiquidityChangeCollectsWithoutHookInterference() (gas: 1114818)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 105.35ms (207.31ms CPU time)\n\nRan 28 tests for test/DeezHook.t.sol:DeezHookToken0Test\n[PASS] testFuzz_buyExactInputFeeIsExact(uint96) (runs: 256, μ: 770294, ~: 770523)\n[PASS] testFuzz_sellExactInputFeeIsExact(uint96) (runs: 256, μ: 782419, ~: 782584)\n[PASS] test_buyExactInputTakesTwoPercentOfTheImdPaid() (gas: 804028)\n[PASS] test_buyExactOutputOnAFreshManagerSeededWithTokensOnly() (gas: 773000)\n[PASS] test_buyExactOutputTakesTwoPercentOfTheImdPaid() (gas: 807312)\n[PASS] test_buyOnAFreshManagerSeededWithTokensOnly() (gas: 953247)\n[PASS] test_callbacksRefuseCallersOtherThanThePoolManager() (gas: 122950)\n[PASS] test_constructorRefusesZeroAndSameAddresses() (gas: 9036)\n[PASS] test_constructorWiresTheDistributor() (gas: 54167)\n[PASS] test_deezSpecifiedPartialFillsAreChargedOnWhatActuallyTraded() (gas: 782231)\n[PASS] test_exactInputBuyStoppedByThePriceLimitReverts() (gas: 915662)\n[PASS] test_exactOutputSellBeyondThePoolsImdReverts() (gas: 870500)\n[PASS] test_exactOutputSellStoppedByThePriceLimitReverts() (gas: 628934)\n[PASS] test_exactOutputSellThatFillsOnlyPartOfTheRequestReverts() (gas: 955419)\n[PASS] test_feeDeltasSumToZeroForTheHook() (gas: 1403852)\n[PASS] test_feesAccumulateAcrossSwaps() (gas: 962171)\n[PASS] test_feesAreRecordedAgainstTheEpochTheyWereTradedIn() (gas: 1283991)\n[PASS] test_harvestMovesFeesToTheDistributorAndCreditsTheEpoch() (gas: 1128998)\n[PASS] test_harvestWithNothingPendingIsANoop() (gas: 43121)\n[PASS] test_hookHasNoOwner() (gas: 54392)\n[PASS] test_initializesFromAnyCallerOnTheLaunchPoolOnce() (gas: 20405597)\n[PASS] test_oldEpochsAreHarvestedByNumber() (gas: 1060042)\n[PASS] test_permissionsMatchTheAddress() (gas: 9154)\n[PASS] test_refusesAPoolWithOtherCurrencies() (gas: 21015718)\n[PASS] test_refusesASecondPool() (gas: 46701)\n[PASS] test_sellExactInputTakesTwoPercentOfTheImdReceived() (gas: 808824)\n[PASS] test_sellExactOutputTakesTwoPercentOfTheImdReceived() (gas: 796847)\n[PASS] test_zeroLiquidityChangeCollectsWithoutHookInterference() (gas: 1115173)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 105.49ms (131.93ms CPU time)\n\nRan 7 test suites in 107.02ms (563.67ms CPU time): 117 tests passed, 0 failed, 0 skipped (117 total tests)\n","passed":true},{"durationMs":73,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"DeezHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"DeezHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"DeezHook.harvest()\",\"DeezHook.harvestEpochs(uint256[])\",\"DeezHook.unlockCallback(bytes)\",\"DeezLiquidityLock.lock()\",\"DeezLiquidityLock.lockAs(uint8,int24,int24)\",\"DeezLiquidityLock.unlockCallback(bytes)\",\"DeezRewards.accrue()\",\"DeezRewards.claim(address,uint256[])\",\"DeezRewards.credit(uint256,uint256)\",\"DeezRewards.finalize(uint256)\",\"DeezRewards.recycle(uint256)\",\"DeezToken.approve(address,uint256)\",\"DeezToken.transfer(address,uint256)\",\"DeezToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"README.md\":299,\"foundry.toml\":20,\"remappings.txt\":5,\"site/deez.config.js\":21,\"site/index.html\":317,\"site/logo.svg\":10,\"src/DeezHook.sol\":341,\"src/DeezLiquidityLock.sol\":193,\"src/DeezRewards.sol\":262,\"src/DeezToken.sol\":144,\"src/HookFlags.sol\":34,\"test/DeezBase.t.sol\":123,\"test/DeezFlywheel.t.sol\":150,\"test/DeezHook.t.sol\":387,\"test/DeezLiquidityLock.t.sol\":175,\"test/DeezRewards.t.sol\":448,\"test/DeezToken.t.sol\":174,\"test/mocks/MockERC20.sol\":15,\"test/mocks/MockHook.sol\":36,\"test/mocks/Swapper.sol\":85},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":4450,"exitCode":0,"name":"slither","output":"[high/high] uninitialized-state at src/DeezToken.sol:86: DeezToken._history (src/DeezToken.sol#36) is never initialized. It is used in:\n[medium/medium] divide-before-multiply at src/DeezLiquidityLock.sol:183: DeezLiquidityLock._floorToSpacing(int24,int24) (src/DeezLiquidityLock.sol#183-187) performs a multiplication on the result of a division:\n[medium/high] incorrect-equality at src/DeezLiquidityLock.sol:92: DeezLiquidityLock.lockAs(DeezLiquidityLock.Mode,int24,int24) (src/DeezLiquidityLock.sol#92-124) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/DeezLiquidityLock.sol:92: DeezLiquidityLock.lockAs(DeezLiquidityLock.Mode,int24,int24) (src/DeezLiquidityLock.sol#92-124) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/DeezToken.sol:109: DeezToken._checkpoint(address) (src/DeezToken.sol#109-126) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/DeezLiquidityLock.sol:92: DeezLiquidityLock.lockAs(DeezLiquidityLock.Mode,int24,int24) (src/DeezLiquidityLock.sol#92-124) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/DeezLiquidityLock.sol:92: DeezLiquidityLock.lockAs(DeezLiquidityLock.Mode,int24,int24) (src/DeezLiquidityLock.sol#92-124) uses a dangerous strict equality:\n[medium/medium] unused-return at src/DeezLiquidityLock.sol:142: DeezLiquidityLock._settle(Currency,int128) (src/DeezLiquidityLock.sol#142-148) ignores return value by poolManager.settle() (src/DeezLiquidityLock.sol#147)\n[medium/medium] unused-return at src/DeezHook.sol:212: DeezHook._harvest(uint256[]) (src/DeezHook.sol#212-228) ignores return value by poolManager.unlock(abi.encode(amount)) (src/DeezHook.sol#223)\n[medium/medium] unused-return at src/DeezRewards.sol:212: DeezRewards.claim(address,uint256[]) (src/DeezRewards.sol#212-233) ignores return value by IDeezHarvester(hook).harvestEpochs(epochs) (src/DeezRewards.sol#215)\n[medium/medium] unused-return at src/DeezHook.sol:212: DeezHook._harvest(uint256[]) (src/DeezHook.sol#212-228) ignores return value by rewards.accrue() (src/DeezHook.sol#222)\n[medium/medium] unused-return at src/DeezLiquidityLock.sol:127: DeezLiquidityLock.unlockCallback(bytes) (src/DeezLiquidityLock.sol#127-140) ignores return value by (delta,None) = poolManager.modifyLiquidity(key,ModifyLiquidityParams({tickLower:tickLower,tickUpper:tickUpper,liquidityDelta:int256(uint256(liquidity)),salt:bytes32(0)}),) (src/DeezLiquidityLock.sol#130-136)\n[medium/medium] unused-return at src/DeezLiquidityLock.sol:92: DeezLiquidityLock.lockAs(DeezLiquidityLock.Mode,int24,int24) (src/DeezLiquidityLock.sol#92-124) ignores return value by (sqrtPriceX96,tick,None,None) = poolManager.getSlot0(key.toId()) (src/DeezLiquidityLock.sol#101)\n[low/medium] calls-loop at src/DeezHook.sol:212: DeezHook._harvest(uint256[]) (src/DeezHook.sol#212-228) has external calls inside a loop: rewards.credit(epochs[i_scope_0],amounts[i_scope_0]) (src/DeezHook.sol#225)\n[low/medium] calls-loop at src/DeezHook.sol:212: DeezHook._harvest(uint256[]) (src/DeezHook.sol#212-228) has external calls inside a loop: rewards.credit(epochs[i_scope_0],amounts[i_scope_0]) (src/DeezHook.sol#225)\n[low/medium] calls-loop at src/DeezRewards.sol:137: DeezRewards.eligibleSupplyAt(uint256) (src/DeezRewards.sol#137-143) has external calls inside a loop: total = token.getCumulativeTotalSupply(start + EPOCH_LENGTH) - token.getCumulativeTotalSupply(start) (src/DeezRewards.sol#139)\n[low/medium] calls-loop at src/DeezRewards.sol:148: DeezRewards.claimable(address,uint256) (src/DeezRewards.sol#148-157) has external calls inside a loop: total = pot.total + IDeezHarvester(hook).pendingFeesOf(epoch) (src/DeezRewards.sol#152)\n[low/medium] calls-loop at src/DeezRewards.sol:130: DeezRewards.weightOf(address,uint256) (src/DeezRewards.sol#130-134) has external calls inside a loop: token.getCumulativeBalanceOf(account,start + EPOCH_LENGTH) - token.getCumulativeBalanceOf(account,start) (src/DeezRewards.sol#132-133)\n[low/medium] calls-loop at src/DeezRewards.sol:130: DeezRewards.weightOf(address,uint256) (src/DeezRewards.sol#130-134) has external calls inside a loop: token.getCumulativeBalanceOf(account,start + EPOCH_LENGTH) - token.getCumulativeBalanceOf(account,start) (src/DeezRewards.sol#132-133)\n[low/medium] calls-loop at src/DeezRewards.sol:137: DeezRewards.eligibleSupplyAt(uint256) (src/DeezRewards.sol#137-143) has external calls inside a loop: total = token.getCumulativeTotalSupply(start + EPOCH_LENGTH) - token.getCumulativeTotalSupply(start) (src/DeezRewards.sol#139)\n[low/medium] reentrancy-benign at src/DeezRewards.sol:212: Reentrancy in DeezRewards.claim(address,uint256[]) (src/DeezRewards.sol#212-233):\n[low/medium] reentrancy-benign at src/DeezLiquidityLock.sol:92: Reentrancy in DeezLiquidityLock.lockAs(DeezLiquidityLock.Mode,int24,int24) (src/DeezLiquidityLock.sol#92-124):\n[low/medium] reentrancy-benign at src/DeezHook.sol:174: Reentrancy in DeezHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/DeezHook.sol#174-192):\n[low/medium] reentrancy-events at src/DeezLiquidityLock.sol:92: Reentrancy in DeezLiquidityLock.lockAs(DeezLiquidityLock.Mode,int24,int24) (src/DeezLiquidityLock.sol#92-124):\n[low/medium] reentrancy-events at src/DeezHook.sol:174: Reentrancy in DeezHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/DeezHook.sol#174-192):\n[low/medium] reentrancy-events at src/DeezHook.sol:212: Reentrancy in DeezHook._harvest(uint256[]) (src/DeezHook.sol#212-228):\n[low/medium] timestamp at src/DeezRewards.sol:185: DeezRewards.credit(uint256,uint256) (src/DeezRewards.sol#185-193) uses timestamp for comparisons\n[low/medium] timestamp at src/DeezToken.sol:56: DeezToken.getPastBalanceOf(address,uint256) (src/DeezToken.sol#56-61) uses timestamp for comparisons\n[low/medium] timestamp at src/DeezRewards.sol:148: DeezRewards.claimable(address,uint256) (src/DeezRewards.sol#148-157) uses timestamp for comparisons\n[low/medium] timestamp at src/DeezRewards.sol:119: DeezRewards.hasEnded(uint256) (src/DeezRewards.sol#119-121) uses timestamp for comparisons\n[low/medium] timestamp at src/DeezToken.sol:78: DeezToken.getCumulativeTotalSupply(uint256) (src/DeezToken.sol#78-83) uses timestamp for comparisons\n[low/medium] timestamp at src/DeezToken.sol:68: DeezToken.getCumulativeBalanceOf(address,uint256) (src/DeezToken.sol#68-74) uses timestamp for comparisons\n[low/medium] timestamp at src/DeezRewards.sol:236: DeezRewards.recycle(uint256) (src/DeezRewards.sol#236-246) uses timestamp for comparisons\n[low/medium] timestamp at src/DeezToken.sol:109: DeezToken._checkpoint(address) (src/DeezToken.sol#109-126) uses timestamp for comparisons","passed":true},{"durationMs":531,"exitCode":0,"name":"aderyn","output":"[high] reentrancy-state-change at src/DeezHook.sol:185: Reentrancy: State change after external call (10 places)\n[high] unsafe-casting at src/DeezRewards.sol:176: Unsafe Casting of integers (2 places)\n[low] costly-loop at src/DeezHook.sol:214: Costly operations inside loop (2 places)\n[low] internal-function-used-once at src/HookFlags.sol:26: Internal Function Used Only Once\n[low] large-numeric-literal at src/DeezHook.sol:47: Large Numeric Literal (3 places)\n[low] modifier-used-only-once at src/DeezLiquidityLock.sol:62: Modifier Invoked Only Once\n[low] require-revert-in-loop at src/DeezRewards.sol:217: Loop Contains `require`/`revert`\n[low] state-change-without-event at src/DeezHook.sol:133: State Change Without Event\n[low] unchecked-return at src/DeezHook.sol:222: Unchecked Return (5 places)\n[low] unused-import at src/DeezLiquidityLock.sol:9: Unused Import","passed":true},{"durationMs":10412,"exitCode":0,"name":"proof 3724fe83d79f","output":"Compiling 87 files with Solc 0.8.26\nSolc 0.8.26 finished in 9.81s\nCompiler run successful!\n\nRan 1 test for test/imd-proof-0626a986/Proof_3724fe83d79f.t.sol:LateHarvestProof\n[PASS] test_earlyClaimerLosesFeesHarvestedIntoTheEpochLater() (gas: 1154545)\nLogs:\n  alice first claim: 75374768530338346334\n  alice second claim: 0\n  alice owed for the epoch: 75374768530338346334\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 8.00ms (619.09µs CPU time)\n\nRan 1 test suite in 8.88ms (8.00ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)\n","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"a5abbeb31c773fa5ccf67944a6055f27a35834c5b77f8f9716ad01f9db0475be","verifiedTreeHash":"e052d91423cf3d99cc7db7d0b42f024a7c242428","verifierVersion":"0.1.0+a6ee6548"},{"checks":[{"durationMs":32403,"exitCode":0,"name":"build","output":"Compiling 92 files with Solc 0.8.26\nSolc 0.8.26 finished in 32.31s\nCompiler run successful!\n","passed":true},{"durationMs":190,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 24 tests for test/DeezRewards.t.sol:DeezRewardsTest\n[PASS] testFuzz_claimsNeverExceedThePotAndSplitByBalance(uint96,uint96,uint96) (runs: 256, μ: 838100, ~: 838238)\n[PASS] testFuzz_claimsNeverExceedThePotWithMidEpochMoves(uint96,uint32,uint32,uint96) (runs: 256, μ: 1195692, ~: 1195787)\n[PASS] test_accrueCreditsTheCurrentEpochOnce() (gas: 195007)\n[PASS] test_balanceMovedAfterTheEpochDoesNotChangeIt() (gas: 746713)\n[PASS] test_claimBeforeTheEpochEndsReverts() (gas: 336533)\n[PASS] test_claimHarvestsOnlyTheEpochsItPays() (gas: 617087)\n[PASS] test_claimHarvestsThePendingFeesOfTheEpochFirst() (gas: 972560)\n[PASS] test_claimManyEpochsAtOnce() (gas: 1055389)\n[PASS] test_claimsAreProportionalToTheAverageBalance() (gas: 1288158)\n[PASS] test_constructorRefusesZeroAddresses() (gas: 10913)\n[PASS] test_creditGoesToTheEpochTheHookNames() (gas: 431309)\n[PASS] test_creditRefusesOthersAndUnfundedAmounts() (gas: 221353)\n[PASS] test_creditToAClosedOrFutureEpochGoesToTheCurrentOne() (gas: 406015)\n[PASS] test_deployerShareIsProportionalToo() (gas: 674502)\n[PASS] test_doubleClaimPaysNothingMore() (gas: 820604)\n[PASS] test_emptyClaimReverts() (gas: 381656)\n[PASS] test_epochArithmetic() (gas: 31377)\n[PASS] test_excludedAddresses() (gas: 33214)\n[PASS] test_excludedAddressesCannotClaim() (gas: 249694)\n[PASS] test_finalizeRequiresAnEndedEpoch() (gas: 195790)\n[PASS] test_lateCreditIsPaidOnASecondClaim() (gas: 1056780)\n[PASS] test_onlyTheAccountItselfCanClaim() (gas: 840182)\n[PASS] test_recycleFoldsUnclaimedIntoTheCurrentEpoch() (gas: 934426)\n[PASS] test_rewardsAreWeightedByHoldingTime() (gas: 1035184)\nSuite result: ok. 24 passed; 0 failed; 0 skipped; finished in 55.32ms (100.24ms CPU time)\n\nRan 11 tests for test/DeezLiquidityLock.t.sol:DeezLiquidityLockToken0Test\n[PASS] test_lockAsFullRangeAndTheLeftoverSingleSided() (gas: 1191703)\n[PASS] test_lockAsRefusesAPriceOutsideTheCallersBounds() (gas: 931915)\n[PASS] test_lockAsRefusesAnEmptySide() (gas: 628629)\n[PASS] test_lockAsSingleSidedIgnoresDustOnTheOtherSide() (gas: 954747)\n[PASS] test_lockBothBecomesFullRange() (gas: 1249175)\n[PASS] test_lockDeezOnlyBecomesLiquidityAboveThePrice() (gas: 1161767)\n[PASS] test_lockImdOnlyBecomesABuyWallBelowThePrice() (gas: 822866)\n[PASS] test_lockRefusesBeforeThePoolExists() (gas: 20366189)\n[PASS] test_lockRefusesNothing() (gas: 53515)\n[PASS] test_lockedLiquidityCannotBeRemoved() (gas: 1044738)\n[PASS] test_wiring() (gas: 37454)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 78.80ms (21.20ms CPU time)\n\nRan 11 tests for test/DeezToken.t.sol:DeezTokenTest\n[PASS] testFuzz_cumulativeBalanceMatchesHoldingTime(uint96,uint32,uint32) (runs: 256, μ: 283589, ~: 283526)\n[PASS] testFuzz_pastBalanceMatchesBalanceAtThatTime(uint96,uint96,uint32) (runs: 256, μ: 264902, ~: 265144)\n[PASS] test_cumulativeBalanceAllowsTheCurrentSecondButNotTheFuture() (gas: 11964)\n[PASS] test_cumulativeBalanceIntegratesHoldingTime() (gas: 510668)\n[PASS] test_cumulativeBalanceRefusesTheFuture() (gas: 20155)\n[PASS] test_metadataAndSupply() (gas: 51264)\n[PASS] test_noMintOwnerOrPause() (gas: 147107)\n[PASS] test_pastBalanceRefusesTheCurrentSecondAndTheFuture() (gas: 22620)\n[PASS] test_pastBalancesFollowTransfers() (gas: 458770)\n[PASS] test_transferFromRespectsAllowance() (gas: 309071)\n[PASS] test_transferMovesExactlyWhatItWasAsked() (gas: 125405)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 79.26ms (23.19ms CPU time)\n\nRan 28 tests for test/DeezHook.t.sol:DeezHookToken1Test\n[PASS] testFuzz_buyExactInputFeeIsExact(uint96) (runs: 256, μ: 775810, ~: 775946)\n[PASS] testFuzz_sellExactInputFeeIsExact(uint96) (runs: 256, μ: 776898, ~: 777169)\n[PASS] test_buyExactInputTakesTwoPercentOfTheImdPaid() (gas: 809451)\n[PASS] test_buyExactOutputOnAFreshManagerSeededWithTokensOnly() (gas: 782766)\n[PASS] test_buyExactOutputTakesTwoPercentOfTheImdPaid() (gas: 811421)\n[PASS] test_buyOnAFreshManagerSeededWithTokensOnly() (gas: 964326)\n[PASS] test_callbacksRefuseCallersOtherThanThePoolManager() (gas: 122926)\n[PASS] test_constructorRefusesZeroAndSameAddresses() (gas: 9036)\n[PASS] test_constructorWiresTheDistributor() (gas: 54167)\n[PASS] test_deezSpecifiedPartialFillsAreChargedOnWhatActuallyTraded() (gas: 777292)\n[PASS] test_exactInputBuyStoppedByThePriceLimitReverts() (gas: 931494)\n[PASS] test_exactOutputSellBeyondThePoolsImdReverts() (gas: 886466)\n[PASS] test_exactOutputSellStoppedByThePriceLimitReverts() (gas: 624203)\n[PASS] test_exactOutputSellThatFillsOnlyPartOfTheRequestReverts() (gas: 976743)\n[PASS] test_feeDeltasSumToZeroForTheHook() (gas: 1409258)\n[PASS] test_feesAccumulateAcrossSwaps() (gas: 967463)\n[PASS] test_feesAreRecordedAgainstTheEpochTheyWereTradedIn() (gas: 1289283)\n[PASS] test_harvestMovesFeesToTheDistributorAndCreditsTheEpoch() (gas: 1134290)\n[PASS] test_harvestWithNothingPendingIsANoop() (gas: 43121)\n[PASS] test_hookHasNoOwner() (gas: 54392)\n[PASS] test_initializesFromAnyCallerOnTheLaunchPoolOnce() (gas: 192396393)\n[PASS] test_oldEpochsAreHarvestedByNumber() (gas: 1065465)\n[PASS] test_permissionsMatchTheAddress() (gas: 9154)\n[PASS] test_refusesAPoolWithOtherCurrencies() (gas: 193096784)\n[PASS] test_refusesASecondPool() (gas: 46689)\n[PASS] test_sellExactInputTakesTwoPercentOfTheImdReceived() (gas: 803381)\n[PASS] test_sellExactOutputTakesTwoPercentOfTheImdReceived() (gas: 792718)\n[PASS] test_zeroLiquidityChangeCollectsWithoutHookInterference() (gas: 1114818)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 79.18ms (178.53ms CPU time)\n\nRan 4 tests for test/DeezFlywheel.t.sol:DeezFlywheelTest\n[PASS] test_earlyClaimerGetsItsShareOfFeesHarvestedLater() (gas: 2161108)\n[PASS] test_feesBelongToTheEpochTheyWereTradedInWheneverHarvested() (gas: 1164796)\n[PASS] test_lastSecondBuyerCannotSnipeTheEpochsPot() (gas: 1651147)\n[PASS] test_tradesBecomeHolderRewards() (gas: 1947513)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 95.52ms (3.79ms CPU time)\n\nRan 11 tests for test/DeezLiquidityLock.t.sol:DeezLiquidityLockToken1Test\n[PASS] test_lockAsFullRangeAndTheLeftoverSingleSided() (gas: 1195357)\n[PASS] test_lockAsRefusesAPriceOutsideTheCallersBounds() (gas: 926515)\n[PASS] test_lockAsRefusesAnEmptySide() (gas: 628626)\n[PASS] test_lockAsSingleSidedIgnoresDustOnTheOtherSide() (gas: 958405)\n[PASS] test_lockBothBecomesFullRange() (gas: 1249163)\n[PASS] test_lockDeezOnlyBecomesLiquidityAboveThePrice() (gas: 1183014)\n[PASS] test_lockImdOnlyBecomesABuyWallBelowThePrice() (gas: 819228)\n[PASS] test_lockRefusesBeforeThePoolExists() (gas: 192355329)\n[PASS] test_lockRefusesNothing() (gas: 53515)\n[PASS] test_lockedLiquidityCannotBeRemoved() (gas: 1044726)\n[PASS] test_wiring() (gas: 37454)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 95.51ms (78.57ms CPU time)\n\nRan 28 tests for test/DeezHook.t.sol:DeezHookToken0Test\n[PASS] testFuzz_buyExactInputFeeIsExact(uint96) (runs: 256, μ: 770294, ~: 770523)\n[PASS] testFuzz_sellExactInputFeeIsExact(uint96) (runs: 256, μ: 782419, ~: 782584)\n[PASS] test_buyExactInputTakesTwoPercentOfTheImdPaid() (gas: 804028)\n[PASS] test_buyExactOutputOnAFreshManagerSeededWithTokensOnly() (gas: 773000)\n[PASS] test_buyExactOutputTakesTwoPercentOfTheImdPaid() (gas: 807312)\n[PASS] test_buyOnAFreshManagerSeededWithTokensOnly() (gas: 953247)\n[PASS] test_callbacksRefuseCallersOtherThanThePoolManager() (gas: 122950)\n[PASS] test_constructorRefusesZeroAndSameAddresses() (gas: 9036)\n[PASS] test_constructorWiresTheDistributor() (gas: 54167)\n[PASS] test_deezSpecifiedPartialFillsAreChargedOnWhatActuallyTraded() (gas: 782231)\n[PASS] test_exactInputBuyStoppedByThePriceLimitReverts() (gas: 915662)\n[PASS] test_exactOutputSellBeyondThePoolsImdReverts() (gas: 870500)\n[PASS] test_exactOutputSellStoppedByThePriceLimitReverts() (gas: 628934)\n[PASS] test_exactOutputSellThatFillsOnlyPartOfTheRequestReverts() (gas: 955419)\n[PASS] test_feeDeltasSumToZeroForTheHook() (gas: 1403852)\n[PASS] test_feesAccumulateAcrossSwaps() (gas: 962171)\n[PASS] test_feesAreRecordedAgainstTheEpochTheyWereTradedIn() (gas: 1283991)\n[PASS] test_harvestMovesFeesToTheDistributorAndCreditsTheEpoch() (gas: 1128998)\n[PASS] test_harvestWithNothingPendingIsANoop() (gas: 43121)\n[PASS] test_hookHasNoOwner() (gas: 54392)\n[PASS] test_initializesFromAnyCallerOnTheLaunchPoolOnce() (gas: 20405597)\n[PASS] test_oldEpochsAreHarvestedByNumber() (gas: 1060042)\n[PASS] test_permissionsMatchTheAddress() (gas: 9154)\n[PASS] test_refusesAPoolWithOtherCurrencies() (gas: 21015718)\n[PASS] test_refusesASecondPool() (gas: 46701)\n[PASS] test_sellExactInputTakesTwoPercentOfTheImdReceived() (gas: 808824)\n[PASS] test_sellExactOutputTakesTwoPercentOfTheImdReceived() (gas: 796847)\n[PASS] test_zeroLiquidityChangeCollectsWithoutHookInterference() (gas: 1115173)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 95.49ms (100.38ms CPU time)\n\nRan 7 test suites in 96.24ms (579.07ms CPU time): 117 tests passed, 0 failed, 0 skipped (117 total tests)\n","passed":true},{"durationMs":47,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"DeezHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"DeezHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"DeezHook.harvest()\",\"DeezHook.harvestEpochs(uint256[])\",\"DeezHook.unlockCallback(bytes)\",\"DeezLiquidityLock.lock()\",\"DeezLiquidityLock.lockAs(uint8,int24,int24)\",\"DeezLiquidityLock.unlockCallback(bytes)\",\"DeezRewards.accrue()\",\"DeezRewards.claim(address,uint256[])\",\"DeezRewards.credit(uint256,uint256)\",\"DeezRewards.finalize(uint256)\",\"DeezRewards.recycle(uint256)\",\"DeezToken.approve(address,uint256)\",\"DeezToken.transfer(address,uint256)\",\"DeezToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"README.md\":299,\"foundry.toml\":20,\"launch.json\":31,\"remappings.txt\":5,\"site/deez.config.js\":21,\"site/index.html\":317,\"site/logo.svg\":10,\"src/DeezHook.sol\":341,\"src/DeezLiquidityLock.sol\":193,\"src/DeezRewards.sol\":262,\"src/DeezToken.sol\":144,\"src/HookFlags.sol\":34,\"test/DeezBase.t.sol\":123,\"test/DeezFlywheel.t.sol\":150,\"test/DeezHook.t.sol\":387,\"test/DeezLiquidityLock.t.sol\":175,\"test/DeezRewards.t.sol\":448,\"test/DeezToken.t.sol\":174,\"test/mocks/MockERC20.sol\":15,\"test/mocks/MockHook.sol\":36,\"test/mocks/Swapper.sol\":85},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"c766cb8e9174d2345adbde115f8f64223ffbee1d67d0ec21716bbb62aab6054c","verifiedTreeHash":"870bc7b125f8ca4bd58e93db619066fe2ad65245","verifierVersion":"0.1.0+a6ee6548"},{"checks":[{"durationMs":54924,"exitCode":0,"name":"build","output":"Compiling 94 files with Solc 0.8.26\nSolc 0.8.26 finished in 54.82s\nCompiler run successful!\n","passed":true},{"durationMs":2009,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 6 tests for test/DeezFailurePaths.t.sol:DeezHookFailurePathsToken1Test\n[PASS] test_dustSwapsTradeWithoutAFee() (gas: 846750)\n[PASS] test_harvestTwiceMovesTheFeesOnce() (gas: 969411)\n[PASS] test_hookCallbacksRefuseBeforeInitialization() (gas: 27644135)\n[PASS] test_lockSpendsAlmostEverythingAndKeepsItLocked() (gas: 960555)\n[PASS] test_swapCallbacksRefuseAnyOtherPoolKey() (gas: 61939)\n[PASS] test_unlockCallbacksRefuseEveryoneButThePoolManager() (gas: 71544)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 28.44ms (22.34ms CPU time)\n\nRan 3 tests for test/DeezFlywheel.t.sol:DeezFlywheelTest\n[PASS] test_feesBelongToTheEpochTheyWereTradedInWheneverHarvested() (gas: 1155995)\n[PASS] test_lastSecondBuyerCannotSnipeTheEpochsPot() (gas: 1637019)\n[PASS] test_tradesBecomeHolderRewards() (gas: 1933397)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 29.22ms (2.94ms CPU time)\n\nRan 6 tests for test/DeezFailurePaths.t.sol:DeezHookFailurePathsToken0Test\n[PASS] test_dustSwapsTradeWithoutAFee() (gas: 840537)\n[PASS] test_harvestTwiceMovesTheFeesOnce() (gas: 963988)\n[PASS] test_hookCallbacksRefuseBeforeInitialization() (gas: 57687382)\n[PASS] test_lockSpendsAlmostEverythingAndKeepsItLocked() (gas: 960567)\n[PASS] test_swapCallbacksRefuseAnyOtherPoolKey() (gas: 61951)\n[PASS] test_unlockCallbacksRefuseEveryoneButThePoolManager() (gas: 71556)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 46.00ms (31.90ms CPU time)\n\nRan 11 tests for test/DeezLiquidityLock.t.sol:DeezLiquidityLockToken1Test\n[PASS] test_lockAsFullRangeAndTheLeftoverSingleSided() (gas: 1195357)\n[PASS] test_lockAsRefusesAPriceOutsideTheCallersBounds() (gas: 926515)\n[PASS] test_lockAsRefusesAnEmptySide() (gas: 628626)\n[PASS] test_lockAsSingleSidedIgnoresDustOnTheOtherSide() (gas: 958405)\n[PASS] test_lockBothBecomesFullRange() (gas: 1249163)\n[PASS] test_lockDeezOnlyBecomesLiquidityAboveThePrice() (gas: 1183014)\n[PASS] test_lockImdOnlyBecomesABuyWallBelowThePrice() (gas: 819228)\n[PASS] test_lockRefusesBeforeThePoolExists() (gas: 27725120)\n[PASS] test_lockRefusesNothing() (gas: 53515)\n[PASS] test_lockedLiquidityCannotBeRemoved() (gas: 1044726)\n[PASS] test_wiring() (gas: 37454)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 47.24ms (36.23ms CPU time)\n\nRan 28 tests for test/DeezHook.t.sol:DeezHookToken1Test\n[PASS] testFuzz_buyExactInputFeeIsExact(uint96) (runs: 256, μ: 775821, ~: 775987)\n[PASS] testFuzz_sellExactInputFeeIsExact(uint96) (runs: 256, μ: 776915, ~: 777173)\n[PASS] test_buyExactInputTakesTwoPercentOfTheImdPaid() (gas: 809451)\n[PASS] test_buyExactOutputOnAFreshManagerSeededWithTokensOnly() (gas: 782766)\n[PASS] test_buyExactOutputTakesTwoPercentOfTheImdPaid() (gas: 811421)\n[PASS] test_buyOnAFreshManagerSeededWithTokensOnly() (gas: 964326)\n[PASS] test_callbacksRefuseCallersOtherThanThePoolManager() (gas: 122926)\n[PASS] test_constructorRefusesZeroAndSameAddresses() (gas: 9036)\n[PASS] test_constructorWiresTheDistributor() (gas: 54167)\n[PASS] test_deezSpecifiedPartialFillsAreChargedOnWhatActuallyTraded() (gas: 777292)\n[PASS] test_exactInputBuyStoppedByThePriceLimitReverts() (gas: 931494)\n[PASS] test_exactOutputSellBeyondThePoolsImdReverts() (gas: 886466)\n[PASS] test_exactOutputSellStoppedByThePriceLimitReverts() (gas: 624203)\n[PASS] test_exactOutputSellThatFillsOnlyPartOfTheRequestReverts() (gas: 976743)\n[PASS] test_feeDeltasSumToZeroForTheHook() (gas: 1409258)\n[PASS] test_feesAccumulateAcrossSwaps() (gas: 967463)\n[PASS] test_feesAreRecordedAgainstTheEpochTheyWereTradedIn() (gas: 1289283)\n[PASS] test_harvestMovesFeesToTheDistributorAndCreditsTheEpoch() (gas: 1134290)\n[PASS] test_harvestWithNothingPendingIsANoop() (gas: 43121)\n[PASS] test_hookHasNoOwner() (gas: 54392)\n[PASS] test_initializesFromAnyCallerOnTheLaunchPoolOnce() (gas: 27764674)\n[PASS] test_oldEpochsAreHarvestedByNumber() (gas: 1065465)\n[PASS] test_permissionsMatchTheAddress() (gas: 9154)\n[PASS] test_refusesAPoolWithOtherCurrencies() (gas: 28381983)\n[PASS] test_refusesASecondPool() (gas: 46689)\n[PASS] test_sellExactInputTakesTwoPercentOfTheImdReceived() (gas: 803381)\n[PASS] test_sellExactOutputTakesTwoPercentOfTheImdReceived() (gas: 792718)\n[PASS] test_zeroLiquidityChangeCollectsWithoutHookInterference() (gas: 1114818)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 53.90ms (126.87ms CPU time)\n\nRan 11 tests for test/DeezToken.t.sol:DeezTokenTest\n[PASS] testFuzz_cumulativeBalanceMatchesHoldingTime(uint96,uint32,uint32) (runs: 256, μ: 283591, ~: 283550)\n[PASS] testFuzz_pastBalanceMatchesBalanceAtThatTime(uint96,uint96,uint32) (runs: 256, μ: 264901, ~: 265144)\n[PASS] test_cumulativeBalanceAllowsTheCurrentSecondButNotTheFuture() (gas: 11964)\n[PASS] test_cumulativeBalanceIntegratesHoldingTime() (gas: 510668)\n[PASS] test_cumulativeBalanceRefusesTheFuture() (gas: 20155)\n[PASS] test_metadataAndSupply() (gas: 51264)\n[PASS] test_noMintOwnerOrPause() (gas: 147107)\n[PASS] test_pastBalanceRefusesTheCurrentSecondAndTheFuture() (gas: 22620)\n[PASS] test_pastBalancesFollowTransfers() (gas: 458770)\n[PASS] test_transferFromRespectsAllowance() (gas: 309071)\n[PASS] test_transferMovesExactlyWhatItWasAsked() (gas: 125405)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 61.08ms (86.28ms CPU time)\n\nRan 21 tests for test/DeezRewards.t.sol:DeezRewardsTest\n[PASS] testFuzz_claimsNeverExceedThePotAndSplitByBalance(uint96,uint96,uint96) (runs: 256, μ: 799567, ~: 799631)\n[PASS] testFuzz_claimsNeverExceedThePotWithMidEpochMoves(uint96,uint32,uint32,uint96) (runs: 256, μ: 1135700, ~: 1135790)\n[PASS] test_accrueCreditsTheCurrentEpochOnce() (gas: 194963)\n[PASS] test_balanceMovedAfterTheEpochDoesNotChangeIt() (gas: 726257)\n[PASS] test_claimBeforeTheEpochEndsReverts() (gas: 311025)\n[PASS] test_claimManyEpochsAtOnce() (gas: 1012203)\n[PASS] test_claimsAreProportionalToTheAverageBalance() (gas: 1241293)\n[PASS] test_constructorRefusesZeroAddresses() (gas: 10847)\n[PASS] test_creditGoesToTheEpochTheHookNames() (gas: 431243)\n[PASS] test_creditRefusesOthersAndUnfundedAmounts() (gas: 221309)\n[PASS] test_creditToAClosedOrFutureEpochGoesToTheCurrentOne() (gas: 405994)\n[PASS] test_deployerShareIsProportionalToo() (gas: 635951)\n[PASS] test_doubleClaimReverts() (gas: 541736)\n[PASS] test_emptyClaimReverts() (gas: 369131)\n[PASS] test_epochArithmetic() (gas: 31377)\n[PASS] test_excludedAddresses() (gas: 33170)\n[PASS] test_excludedAddressesCannotClaim() (gas: 249522)\n[PASS] test_finalizeRequiresAnEndedEpoch() (gas: 195746)\n[PASS] test_onlyTheAccountItselfCanClaim() (gas: 807218)\n[PASS] test_recycleFoldsUnclaimedIntoTheCurrentEpoch() (gas: 887592)\n[PASS] test_rewardsAreWeightedByHoldingTime() (gas: 996604)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 61.08ms (121.25ms CPU time)\n\nRan 20 tests for test/DeezFailurePaths.t.sol:DeezRewardsFailurePathsTest\n[PASS] testFuzz_allHoldersTogetherNeverDrainMoreThanThePot(uint96,uint96,uint128,uint32,uint32) (runs: 512, μ: 863991, ~: 923093)\n[PASS] test_accrueWithNothingNewCreditsNothing() (gas: 222124)\n[PASS] test_balanceMovedInTheFirstSecondOfTheNextEpochDoesNotCount() (gas: 528892)\n[PASS] test_batchWithAnUnendedEpochPaysNothing() (gas: 371417)\n[PASS] test_closedEpochRefusesClaimsAndASecondRecycle() (gas: 303462)\n[PASS] test_creditCannotBeForgedOrOverdrawn() (gas: 209779)\n[PASS] test_duplicateEpochInOneBatchRevertsTheWholeBatch() (gas: 523698)\n[PASS] test_emptyBatchReverts() (gas: 198517)\n[PASS] test_epochWithNoEligibleSupplyPaysNobodyAndIsRecycled() (gas: 624519)\n[PASS] test_excludedAddressesCannotClaimEvenForThemselves() (gas: 334559)\n[PASS] test_finalizeIsIdempotentAndIgnoresLaterTransfers() (gas: 392308)\n[PASS] test_holderWithNoBalanceInTheEpochHasNothingToClaim() (gas: 329759)\n[PASS] test_lastSecondBuyerWeighsOneSecond() (gas: 713658)\n[PASS] test_nobodyCanClaimForSomeoneElse() (gas: 413755)\n[PASS] test_recycleOfAnEpochThatNeverHadAPotClosesItForNothing() (gas: 99884)\n[PASS] test_recycleOpensExactlyAfterTheClaimWindow() (gas: 419810)\n[PASS] test_recycledRewardsArePaidByTheNewEpochsBalances() (gas: 518266)\n[PASS] test_shareThatRoundsToZeroIsRefusedNotPaid() (gas: 460000)\n[PASS] test_unaccruedDonationGoesToTheCurrentEpochNotAnEndedOne() (gas: 402327)\n[PASS] test_weightReadsRevertUntilTheEpochHasEnded() (gas: 222940)\nSuite result: ok. 20 passed; 0 failed; 0 skipped; finished in 63.40ms (69.80ms CPU time)\n\nRan 11 tests for test/DeezLiquidityLock.t.sol:DeezLiquidityLockToken0Test\n[PASS] test_lockAsFullRangeAndTheLeftoverSingleSided() (gas: 1191703)\n[PASS] test_lockAsRefusesAPriceOutsideTheCallersBounds() (gas: 931915)\n[PASS] test_lockAsRefusesAnEmptySide() (gas: 628629)\n[PASS] test_lockAsSingleSidedIgnoresDustOnTheOtherSide() (gas: 954747)\n[PASS] test_lockBothBecomesFullRange() (gas: 1249175)\n[PASS] test_lockDeezOnlyBecomesLiquidityAboveThePrice() (gas: 1161767)\n[PASS] test_lockImdOnlyBecomesABuyWallBelowThePrice() (gas: 822866)\n[PASS] test_lockRefusesBeforeThePoolExists() (gas: 57766125)\n[PASS] test_lockRefusesNothing() (gas: 53515)\n[PASS] test_lockedLiquidityCannotBeRemoved() (gas: 1044738)\n[PASS] test_wiring() (gas: 37454)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 71.21ms (34.96ms CPU time)\n\nRan 28 tests for test/DeezHook.t.sol:DeezHookToken0Test\n[PASS] testFuzz_buyExactInputFeeIsExact(uint96) (runs: 256, μ: 770307, ~: 770527)\n[PASS] testFuzz_sellExactInputFeeIsExact(uint96) (runs: 256, μ: 782431, ~: 782616)\n[PASS] test_buyExactInputTakesTwoPercentOfTheImdPaid() (gas: 804028)\n[PASS] test_buyExactOutputOnAFreshManagerSeededWithTokensOnly() (gas: 773000)\n[PASS] test_buyExactOutputTakesTwoPercentOfTheImdPaid() (gas: 807312)\n[PASS] test_buyOnAFreshManagerSeededWithTokensOnly() (gas: 953247)\n[PASS] test_callbacksRefuseCallersOtherThanThePoolManager() (gas: 122950)\n[PASS] test_constructorRefusesZeroAndSameAddresses() (gas: 9036)\n[PASS] test_constructorWiresTheDistributor() (gas: 54167)\n[PASS] test_deezSpecifiedPartialFillsAreChargedOnWhatActuallyTraded() (gas: 782231)\n[PASS] test_exactInputBuyStoppedByThePriceLimitReverts() (gas: 915662)\n[PASS] test_exactOutputSellBeyondThePoolsImdReverts() (gas: 870500)\n[PASS] test_exactOutputSellStoppedByThePriceLimitReverts() (gas: 628934)\n[PASS] test_exactOutputSellThatFillsOnlyPartOfTheRequestReverts() (gas: 955419)\n[PASS] test_feeDeltasSumToZeroForTheHook() (gas: 1403852)\n[PASS] test_feesAccumulateAcrossSwaps() (gas: 962171)\n[PASS] test_feesAreRecordedAgainstTheEpochTheyWereTradedIn() (gas: 1283991)\n[PASS] test_harvestMovesFeesToTheDistributorAndCreditsTheEpoch() (gas: 1128998)\n[PASS] test_harvestWithNothingPendingIsANoop() (gas: 43121)\n[PASS] test_hookHasNoOwner() (gas: 54392)\n[PASS] test_initializesFromAnyCallerOnTheLaunchPoolOnce() (gas: 57806071)\n[PASS] test_oldEpochsAreHarvestedByNumber() (gas: 1060042)\n[PASS] test_permissionsMatchTheAddress() (gas: 9154)\n[PASS] test_refusesAPoolWithOtherCurrencies() (gas: 58445777)\n[PASS] test_refusesASecondPool() (gas: 46701)\n[PASS] test_sellExactInputTakesTwoPercentOfTheImdReceived() (gas: 808824)\n[PASS] test_sellExactOutputTakesTwoPercentOfTheImdReceived() (gas: 796847)\n[PASS] test_zeroLiquidityChangeCollectsWithoutHookInterference() (gas: 1115173)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 71.27ms (152.57ms CPU time)\n\nRan 1 test for test/DeezHookInvariant.t.sol:DeezHookInvariantToken1Test\n[PASS]\nDeezHookInvariantToken1Test invariants:\n[PASS] invariant_claimsAreBackedByTheManager\n[PASS] invariant_everyFeeIsPendingOrHarvested\n[PASS] invariant_everyFeeIsTwoPercentOfTheGrossImd\n[PASS] invariant_harvestedImdSitsInTheDistributor\n[PASS] invariant_hookAndDistributorHoldNoDeez\n[PASS] invariant_pendingFeesAreExactlyTheHookClaims\n DeezHookInvariantToken1Test invariants (runs: 64, calls: 2560, reverts: 0)\n\n╭-------------+--------------+-------+---------+----------╮\n| Contract    | Selector     | Calls | Reverts | Discards |\n+=========================================================+\n| HookHandler | buyExactIn   | 371   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | buyExactOut  | 375   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | donate       | 388   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | harvest      | 377   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | sellExactIn  | 340   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | sellExactOut | 348   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | warp         | 361   | 0       | 0        |\n╰-------------+--------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 456.80ms (441.38ms CPU time)\n\nRan 1 test for test/DeezHookInvariant.t.sol:DeezHookInvariantToken0Test\n[PASS]\nDeezHookInvariantToken0Test invariants:\n[PASS] invariant_claimsAreBackedByTheManager\n[PASS] invariant_everyFeeIsPendingOrHarvested\n[PASS] invariant_everyFeeIsTwoPercentOfTheGrossImd\n[PASS] invariant_harvestedImdSitsInTheDistributor\n[PASS] invariant_hookAndDistributorHoldNoDeez\n[PASS] invariant_pendingFeesAreExactlyTheHookClaims\n DeezHookInvariantToken0Test invariants (runs: 64, calls: 2560, reverts: 0)\n\n╭-------------+--------------+-------+---------+----------╮\n| Contract    | Selector     | Calls | Reverts | Discards |\n+=========================================================+\n| HookHandler | buyExactIn   | 371   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | buyExactOut  | 375   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | donate       | 388   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | harvest      | 377   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | sellExactIn  | 340   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | sellExactOut | 348   | 0       | 0        |\n|-------------+--------------+-------+---------+----------|\n| HookHandler | warp         | 361   | 0       | 0        |\n╰-------------+--------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 477.64ms (453.20ms CPU time)\n\nRan 1 test for test/DeezRewardsInvariant.t.sol:DeezRewardsInvariantTest\n[PASS]\nDeezRewardsInvariantTest invariants:\n[PASS] invariant_claimsNeverExceedTheBalanceShare\n[PASS] invariant_closedEpochsNeverReopen\n[PASS] invariant_holdsAtLeastWhatItReserved\n[PASS] invariant_imdIsConserved\n[PASS] invariant_noPotPaysMoreThanItHolds\n[PASS] invariant_onlyTheAccountClaimsItsShare\n[PASS] invariant_reservedEqualsWhatOpenPotsOwe\n[PASS] invariant_supplyNeverChanges\n DeezRewardsInvariantTest invariants (runs: 256, calls: 15360, reverts: 0)\n\n╭----------------+---------------------+-------+---------+----------╮\n| Contract       | Selector            | Calls | Reverts | Discards |\n+===================================================================+\n| RewardsHandler | claim               | 1936  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| RewardsHandler | claimFor            | 1960  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| RewardsHandler | finalize            | 1889  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| RewardsHandler | fund                | 1884  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| RewardsHandler | recycle             | 1942  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| RewardsHandler | transfer            | 1939  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| RewardsHandler | warp                | 1940  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| RewardsHandler | warpPastClaimWindow | 1870  | 0       | 0        |\n╰----------------+---------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.90s (1.90s CPU time)\n\nRan 13 test suites in 1.90s (3.37s CPU time): 148 tests passed, 0 failed, 0 skipped (148 total tests)\n","passed":true},{"durationMs":59,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"DeezHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"DeezHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"DeezHook.harvest()\",\"DeezHook.harvestEpochs(uint256[])\",\"DeezHook.unlockCallback(bytes)\",\"DeezLiquidityLock.lock()\",\"DeezLiquidityLock.lockAs(uint8,int24,int24)\",\"DeezLiquidityLock.unlockCallback(bytes)\",\"DeezRewards.accrue()\",\"DeezRewards.claim(address,uint256[])\",\"DeezRewards.credit(uint256,uint256)\",\"DeezRewards.finalize(uint256)\",\"DeezRewards.recycle(uint256)\",\"DeezToken.approve(address,uint256)\",\"DeezToken.transfer(address,uint256)\",\"DeezToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"README.md\":273,\"foundry.toml\":20,\"remappings.txt\":5,\"site/deez.config.js\":21,\"site/index.html\":317,\"site/logo.svg\":10,\"src/DeezHook.sol\":339,\"src/DeezLiquidityLock.sol\":193,\"src/DeezRewards.sol\":239,\"src/DeezToken.sol\":144,\"src/HookFlags.sol\":34,\"test/DeezBase.t.sol\":123,\"test/DeezFailurePaths.t.sol\":409,\"test/DeezFlywheel.t.sol\":106,\"test/DeezHook.t.sol\":387,\"test/DeezHookInvariant.t.sol\":153,\"test/DeezLiquidityLock.t.sol\":175,\"test/DeezRewards.t.sol\":386,\"test/DeezRewardsInvariant.t.sol\":246,\"test/DeezToken.t.sol\":174,\"test/mocks/MockERC20.sol\":15,\"test/mocks/Swapper.sol\":85},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"d571eca43de6a6261cebe6e76ca73273e730406f579904197bedb346d6fa2218","verifiedTreeHash":"434daa4bd27fe6e2369961029a83e3e60abc2e51","verifierVersion":"0.1.0+a6ee6548"}]}