{"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":"3e287c7b-b983-4427-a05a-197320b17ccc","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"5f4ffc6a5f1e59e5809b19ce7dc03dc60f7de72c29c47a4c5de0a3b0e92ce77c","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":"d8ed774f41b3e3d90db419d71e26f63c2cc64b2351b431cd3740c0c602c53754","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":"0f160e5957e8e288400da7f676a5060e0509f9589176ab11e36889184e9d3316","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":"9f4bf963c6cecdd2d07349591fe2e254fb3ac85ebd5a09cff3f6b05f2079de6e","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":"34afaacf30fe9dee485355faabbe080dfc32f077e5ba8a2f7ca0e00154d186aa","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":"df54acfb9b8fc68a8b0ab43d6a2ad8fa65d1e2ec73aa4eb1b2976cb095f48295","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":"af68e0923b60647308e2942753a5fd31a62e7a2a1f1750684fd48aebdb3a58be","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"8c562e1eb519f87ee1e088b44dff95f1ac37ad95de249f93c905b5c3610fb4d2","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":"Build a simple, lightweight, fully client-side multi-wallet bundler site for generating wallets and preparing token/NFT snipes, plus launch the associated token as a Uniswap v4 hook.\n\nToken / Launch:\n- Name: IMDBundler\n- Symbol: IMDB\n- Standard fixed-supply ERC-20, 18 decimals, total supply 1,000,000,000\n- Immutable, no mint, no owner functions after deploy, no transfer tax\n- Launch kind: univ4_hook on Ethereum mainnet, paired with IMD\n- Standard 10% agent allocation\n- Hook: simple immutable Uniswap v4 hook (beforeSwap + afterSwap with return deltas as needed). Take exactly 2% fee on every buy and every sell (on settled deltas, exact-in and exact-out). Use that 2% to immediately buy back IMDB and burn it in the same flow (or as soon as practical within the hook). No owner, no setters, no upgrades, no pause, fully immutable. Keep all logic inside the hook or contracts it deploys itself in the constructor. No external owner or admin.\n- Pool: fee 12500, tick spacing 60 (or current standard)\n\nSite:\n- Name/label: imdbundler (so it can live at imdbundler.sites.imd.fun)\n- Theme: pure black background (#000000) with lime green accents (#00FF00 / #39FF14). All text, buttons, borders, and highlights in lime green. Minimal, high-contrast, cyber/terminal aesthetic. No other colors except black and lime.\n- Fully client-side only. Private keys and seed phrases never leave the browser. No backend that ever sees keys.\n- Wallet generation: user can generate N wallets (e.g. 5–50) from a random seed or fresh keypairs. Show addresses, allow copy, encrypted local export (password-protected JSON or similar), and clear/reset.\n- Multi-wallet management: list all generated wallets, show balances (via public RPC), fund distribution helpers (user signs from their own external wallet), and prepare individual or batched buy transactions for a target token or NFT contract address.\n- Sniping support: input target contract + amount, generate ready-to-sign buy transactions for each wallet (or a simple multi-wallet bundle prep). Support common chains the swarm can handle (Ethereum mainnet and any others currently supported).\n- Critical requirement — no wallet connect conflicts: do NOT force a single persistent wallet connection that fights with the user’s main wallet or multiple generated wallets. Prefer pure client-side generation + external signing (user pastes tx or uses their own wallet extension only when they choose to fund/sign). If any connect is used, make it optional, isolated, and non-conflicting. Never assume one connected wallet controls everything.\n- Lightweight stack: static or Vite + React + TypeScript + viem (or equivalent). Fast, no heavy frameworks.\n- Pages/sections: Home (generate wallets), Manage (list + export + fund), Snipe (prepare buys), Token info (IMDB contract + hook address, links, supply, burn stats if available), and a short disclaimer that keys are client-side only and users are responsible for their own security.\n- Pepe visuals: include a bunch of Pepe the Frog images in different classic meme poses (thinking, smug, happy, sad, dancing, sunglasses, etc.) scattered tastefully across the site as decorative elements, buttons, backgrounds, or icons. Use public-domain or generated Pepe-style art that fits the lime-on-black theme.\n- Pepe P&L card: add a simple Pepe profit-and-loss card generator. User enters entry price, exit price, amount, or % gain/loss. It renders a shareable card with a Pepe pose matching the result (happy Pepe for profit, sad/thinking for loss), the numbers in lime green on black, and a download button (PNG) so people can share it on socials.\n- Host the site (IPFS + sites.imd.fun), publish source to the launch GitHub repo, and include the live token and hook addresses once deployed.\n\nMake the UI extremely simple, fast, and mobile-friendly. Prioritize security (client-side keys) and zero wallet-connect conflicts above fancy features. Keep the hook simple, fully immutable, and focused on the 2% buyback-and-burn mechanism.\n\nToken name: IMDBundler\nToken symbol: IMDB","parentJobId":null,"planHash":"5bef55de39c04d9ba973acb9f80ad16102b5de4db94a8648dafba3a1b010633d","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"3e287c7b-b983-4427-a05a-197320b17ccc","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-2300-imdbundler"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51401","feedbackHash":"2dfc4ad5bdef12258021ec8efd9353374ef9dfa6edf0ce322521ba299cbba058","nodeKey":"audit_economics","submissionHash":"5f4ffc6a5f1e59e5809b19ce7dc03dc60f7de72c29c47a4c5de0a3b0e92ce77c","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51352","feedbackHash":"cd4af0b1e168b53cc848dc49fb5ce7b19d112da3f0cce8f5c3731b16bce1edaf","nodeKey":"audit_flow","submissionHash":"d8ed774f41b3e3d90db419d71e26f63c2cc64b2351b431cd3740c0c602c53754","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52669","feedbackHash":"66e072de4e7dd4c62622cd9729cc47fdecaecf0cc4940a92cc31bde368e59826","nodeKey":"audit_judge","submissionHash":"4699692304c40a65e3364311242a310b63123591103e2cced9f171e8f9c386fe","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"54154","feedbackHash":"2fbb0b861f7c6d4c6a14f9ecc89cffeaa587f1b0c4dd1ace1cae1b4ab94d9c10","nodeKey":"audit_judge","submissionHash":"0f160e5957e8e288400da7f676a5060e0509f9589176ab11e36889184e9d3316","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51512","feedbackHash":"344881bcb17217339f6b45c22ebb2a4760c5a757ada05af72b91e31e724be9ae","nodeKey":"audit_math","submissionHash":"9f4bf963c6cecdd2d07349591fe2e254fb3ac85ebd5a09cff3f6b05f2079de6e","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"53910","feedbackHash":"783c886537e27025eb5a718b20c993f23997c0f68e5a8ce69dc224f790c08098","nodeKey":"audit_permissions","submissionHash":"34afaacf30fe9dee485355faabbe080dfc32f077e5ba8a2f7ca0e00154d186aa","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51585","feedbackHash":"1498ff679fd57d56d7860d8075c31677cfae527ea6737ca9e9943ef4a8619ce5","nodeKey":"build_contract_project","submissionHash":"beae4f17c2284e4e9e6f1776abaa007ad6c0cbbf8ea5befe95ac6069b828c7e7","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51452","feedbackHash":"0f555a521861f17a4a23dfbadfb120bd653f2a0dd85797cb3719bd42244667bc","nodeKey":"build_contract_project","submissionHash":"df54acfb9b8fc68a8b0ab43d6a2ad8fa65d1e2ec73aa4eb1b2976cb095f48295","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52163","feedbackHash":"1c0441c3916830c6f567fd66df2a09e27564abd076c64d0fa96bf6d3b15b810f","nodeKey":"manifest","submissionHash":"5cb2a792c830c89fa38396ce095a7d1c9d820b5377c7957fe3d590088271d291","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51027","feedbackHash":"d0e34d58fc7206455b30940d9ad7d1713417688beca152125b4cf610494dd4f5","nodeKey":"manifest","submissionHash":"af68e0923b60647308e2942753a5fd31a62e7a2a1f1750684fd48aebdb3a58be","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52628","feedbackHash":"c4ce1c3dddf7221b4b07c8b685c18506ffe23cdcb9c2d66313048cbdb763709d","nodeKey":"write_foundry_tests","submissionHash":"f24803d32ec05ae984379a1f7e5c8a8e757369cbfd4dfa0e5ec2efaebda757e0","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52678","feedbackHash":"190767338dc9e930965b153d07614655e05d549a3e6432633ff7306985f9a139","nodeKey":"write_foundry_tests","submissionHash":"8c562e1eb519f87ee1e088b44dff95f1ac37ad95de249f93c905b5c3610fb4d2","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"24b23a135f13a9802c6d1c61b05f9a74ad0f6377f852e79d7d75113a1b60562a","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"f9a9196633a6ffa8","findings":[],"hash":"0f160e5957e8e288400da7f676a5060e0509f9589176ab11e36889184e9d3316","nodeId":"42ffb81d-480c-4f6a-9fda-cef4d55ba696","outcome":"completed","summary":"Both of my earlier findings are fixed, and I found nothing new. The review is recorded in `.imd-findings.json` with an empty findings list and a coverage row for every entry point.\n\n**Medium, prepaid seller overpays or reverts (841aea96): fixed.** The hook now reads the manager's synced currency in `_burnAvailableTokens` and skips the IMDB take when IMDB is synced, leaving the purchased IMDB as a claim for the next swap or `processFees()`. I copied both attached proofs under test/scratch and ran them against the revised code. All four tests pass, including the two controls. I also wrote my own probe for two cases the author's tests do not cover directly: a prepaid exact-out sell, and a prepaid sell followed by a second plain swap inside the same unlock. In both orderings the seller pays exactly the swap delta, the deferred claim is burned on the second swap, and all deltas and counters reconcile.\n\n**Low, zero-liquidity price walk (03615ec5): fixed.** The buyback now runs only when the pool has in-range liquidity. The author's regression test shows three retries leave slot0 and pending claims unchanged, and the residual walk off the end of a range is documented in docs/HOOK.md.\n\n**Other checks this round:**\n- The hook diff between revisions is exactly the two guards above, nothing else changed in src.\n- The full project suite passes: 112 tests across unit, edge, revision, and stateful invariant suites.\n- The specialists' remaining low and info items (JIT liquidity as buyback counterparty, fee-free hookless pools, dust fee floor) are design properties already documented, not reproducible losses, so I did not open findings for them.\n- launch.json matches the constructor, permissions and flag mask.\n\nThe scratch directory is removed and the working tree is clean apart from the findings file.","treeHash":null,"usage":{"cachedInputTokens":576544,"inputTokens":226,"model":"claude-fable-5-1","outputTokens":12001,"runtime":"claude","turns":19,"wallClockMs":189555}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"be306690d4a9deec","findings":[{"citation":"resolved","description":"_burnAvailableTokens runs inside afterSwap (via _process) and calls poolManager.take(token, hook, amount), moving real IMDB out of the manager in the middle of the swapper's unlock. PoolManager.settle() computes paid = balanceOfSelf() - syncedReserves. A router that uses the valid v4 ordering sync(IMDB) -> transfer(IMDB) -> swap -> settle() (pay-first, as opposed to swap -> sync -> transfer -> settle) is therefore credited input - amountBurned instead of input: its IMDB delta ends at -amountBurned and the unlock reverts with CurrencyNotSettled. The identical router and ordering settle correctly on the same pool without the hook (control test), so the hook introduces an integration asymmetry: sells work only for routers that settle after the swap. Buys are unaffected because the taken currency (IMDB) is not the one synced. This is a liveness/compatibility defect for one integration pattern, not a loss of funds; Uniswap's V4Router actions (SETTLE then SWAP with OPEN_DELTA) complete the settle before swapping and are unaffected. Minimal fix: do not take IMDB during the swap callback. Keep the purchased IMDB as an ERC-6909 claim in afterSwap and redeem+burn it only on the processFees() path (its own unlock, where no third-party sync can be pending), or defer the take when poolManager has a synced currency equal to the token (reservesOf/getSyncedCurrency via TransientStateLibrary).","line":243,"path":"src/IMDBHook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {IMDBToken} from \"src/IMDBToken.sol\";\nimport {IMDBHook} from \"src/IMDBHook.sol\";\nimport {HookFlags} from \"src/HookFlags.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 {SwapParams, ModifyLiquidityParams} from \"v4-core/src/types/PoolOperation.sol\";\nimport {BalanceDelta} from \"v4-core/src/types/BalanceDelta.sol\";\nimport {TickMath} from \"v4-core/src/libraries/TickMath.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract PairMock 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 A router that pays its exact IMDB input first (sync -> transfer), then swaps, then settles.\n/// This ordering is valid on a hookless pool (see the control test) but is broken by IMDBHook,\n/// whose afterSwap physically takes IMDB out of the manager between the sync and the settle.\ncontract PrepayRouter is IUnlockCallback {\n    address constant IMD = 0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7;\n    IPoolManager immutable manager;\n\n    constructor(IPoolManager m) {\n        manager = m;\n    }\n\n    function seed(PoolKey memory key) external {\n        manager.unlock(abi.encode(uint8(0), key, int256(0)));\n    }\n\n    function sell(PoolKey memory key, uint256 amountIn, bool prepay) external returns (BalanceDelta) {\n        return abi.decode(manager.unlock(abi.encode(prepay ? uint8(1) : uint8(2), key, int256(amountIn))), (BalanceDelta));\n    }\n\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        (uint8 mode, PoolKey memory key, int256 amount) = abi.decode(data, (uint8, PoolKey, int256));\n        if (mode == 0) {\n            (BalanceDelta seeded,) =\n                manager.modifyLiquidity(key, ModifyLiquidityParams(-887220, 887220, 1_000_000 ether, 0), \"\");\n            _pay(key.currency0, uint256(-int256(seeded.amount0())));\n            _pay(key.currency1, uint256(-int256(seeded.amount1())));\n            return \"\";\n        }\n        bool tokenIs0 = Currency.unwrap(key.currency0) != IMD;\n        Currency tokenC = tokenIs0 ? key.currency0 : key.currency1;\n        Currency pairC = tokenIs0 ? key.currency1 : key.currency0;\n        SwapParams memory p =\n            SwapParams(tokenIs0, -amount, tokenIs0 ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1);\n        BalanceDelta d;\n        if (mode == 1) {\n            manager.sync(tokenC);\n            tokenC.transfer(address(manager), uint256(amount));\n            d = manager.swap(key, p, \"\");\n            manager.settle();\n        } else {\n            d = manager.swap(key, p, \"\");\n            _pay(tokenC, uint256(amount));\n        }\n        int128 out = tokenIs0 ? d.amount1() : d.amount0();\n        manager.take(pairC, address(this), uint256(uint128(out)));\n        return abi.encode(d);\n    }\n\n    function _pay(Currency c, uint256 amount) private {\n        manager.sync(c);\n        c.transfer(address(manager), amount);\n        manager.settle();\n    }\n}\n\ncontract PrepaidRouterTest is Test {\n    address constant IMD = 0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7;\n    uint160 constant Q96 = 79228162514264337593543950336;\n    IPoolManager manager;\n    IMDBToken token;\n    IMDBHook hook;\n    PrepayRouter router;\n    PoolKey key;\n\n    function setUp() public {\n        manager = IPoolManager(address(new PoolManager(address(this))));\n        vm.etch(IMD, address(new PairMock()).code);\n        token = new IMDBToken();\n        bytes memory creation = abi.encodePacked(type(IMDBHook).creationCode, abi.encode(manager, token));\n        bytes32 h = keccak256(creation);\n        for (uint256 i; i < 300_000; ++i) {\n            address predicted =\n                address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), bytes32(i), h)))));\n            if (!HookFlags.matches(predicted, 0x20cc)) continue;\n            hook = new IMDBHook{salt: bytes32(i)}(manager, token);\n            break;\n        }\n        require(address(hook) != address(0), \"no salt\");\n        key = hook.poolKey();\n        manager.initialize(key, Q96);\n        router = new PrepayRouter(manager);\n        token.transfer(address(router), 500_000_000 ether);\n        PairMock(IMD).mint(address(router), 500_000_000 ether);\n        router.seed(key);\n    }\n\n    /// Control: the same prepaid ordering settles fine on an identical pool without the hook.\n    function test_control_prepaidSellSettlesOnHooklessPool() public {\n        PoolKey memory plain = key;\n        plain.hooks = IHooks(address(0));\n        manager.initialize(plain, Q96);\n        router.seed(plain);\n        router.sell(plain, 100 ether, true);\n    }\n\n    /// Control: swap-then-settle ordering works with the hook.\n    function test_control_swapThenSettleWorksWithHook() public {\n        router.sell(key, 100 ether, false);\n        assertGt(hook.totalFeesIMD(), 0);\n    }\n\n    /// FAILS on the current code: the hook's afterSwap takes IMDB out of the manager after the\n    /// router synced IMDB and transferred its input, so settle() credits (input - burned) and the\n    /// unlock ends with CurrencyNotSettled. Passes once the burn no longer moves IMDB during the swap.\n    function test_sellWithInputPaidBeforeSwap() public {\n        router.sell(key, 100 ether, true);\n        assertGt(hook.totalFeesIMD(), 0);\n    }\n}","reproduction":"State: fresh PoolManager, IMDBToken, IMDBHook mined at flags 0x20cc, pool initialized at sqrtPrice 2^96, full-range liquidity 1e24 seeded, pool already holds IMDB so the burn is not deferred. Router inside its unlockCallback: (1) manager.sync(IMDB); (2) IMDB.transfer(manager, 100e18); (3) manager.swap(key, SwapParams(zeroForOne = tokenIs0, amountSpecified = -100e18, limit = MIN+1 or MAX-1), '') -> hook afterSwap mints IMD fee claim, buys IMDB, burns claim and takes R IMDB out of the manager; (4) manager.settle() -> paid = (reservesBefore + 100e18 - R) - reservesBefore = 100e18 - R; (5) take IMD output. Expected: unlock completes, fee charged, 2% buyback+burn happens. Actual: unlock reverts with CurrencyNotSettled() because the router's IMDB delta is -R. Same sequence with key.hooks = address(0) on the same manager succeeds; same sequence with swap before sync/transfer/settle succeeds with the hook. See test/scratch/PrepaidRouter.t.sol: test_sellWithInputPaidBeforeSwap fails, both controls pass.","severity":"low","snippet":"        uint256 balance = token.balanceOf(address(poolManager));\n        uint256 amount = claim < balance ? claim : balance;\n        if (amount == 0) return;\n        totalBurned += amount;\n        poolManager.burn(address(this), tokenId, amount);\n        poolManager.take(Currency.wrap(address(token)), address(this), amount);","title":"afterSwap physically takes IMDB out of the PoolManager mid-swap, so any router that syncs and pays its IMDB input before calling swap fails with CurrencyNotSettled"},{"citation":"resolved","description":"Trust-gap seam (access x economics): processFees() is callable by anyone and the buyback executes at the current slot0 price with a +/-1% sqrt-price bound (about 2% price). Because the hook declares no liquidity callbacks, an actor can add concentrated liquidity just above spot, call processFees(), sell IMDB to the hook at spot plus the 1.25% LP fee, and remove liquidity, never paying the 2% hook sell fee. The hook still receives and burns IMDB at roughly spot, so no material loss to holders was demonstrated: a sandwich of the buyback is unprofitable because the attacker's own swaps pay 2% + 1.25% each way while the buyback can move price at most about 2%. Recorded for the judge as a trust assumption already stated in docs/SECURITY.md (\"residual contract risks\"); no fix is required for the brief's immutable design.","line":210,"path":"src/IMDBHook.sol","reproduction":"Precondition: pendingIMD() > 0 after a large buy hit the buyback bound (e.g. test_boundedBuybackRetainsAndRetriesClaims: buy 20,000,000e18 IMD exact-in on 1e24 full-range liquidity). Actor: any EOA. Steps: (1) modifyLiquidity on the launch key in the single tick-spacing range just above the current tick with large liquidity (no hook callback runs); (2) call hook.processFees(); the hook burns pendingIMD claims and swaps them through the actor's range at spot; (3) remove the liquidity. Outcome: actor converted IMDB to IMD at spot and earned the 1.25% LP fee on the hook's spend; the hook's IMDB received is no lower than through the pre-existing liquidity. Not a reproducible loss; informational.","severity":"info","snippet":"            (uint160 price,,,) = poolManager.getSlot0(key.toId());\n            uint256 bound = pairIsCurrency0\n                ? uint256(price) * (10_000 - BUYBACK_SQRT_MOVE_BPS) / 10_000\n                : uint256(price) * (10_000 + BUYBACK_SQRT_MOVE_BPS) / 10_000;","title":"Buyback price bound is relative to the manipulable post-trade spot price and processFees is permissionless; JIT liquidity can be the counterparty of every buyback fee-free (documented residual risk, b"}],"hash":"34afaacf30fe9dee485355faabbe080dfc32f077e5ba8a2f7ca0e00154d186aa","nodeId":"93dbdb41-1ce9-446c-8294-b690c42feca8","outcome":"completed","summary":"The review is complete and `.imd-findings.json` is written with the findings and a full coverage record. No source files were changed. The untracked dotfiles in git status are machine-provided device nodes, not my edits, and `.imd-findings.json` plus `test/scratch/` are covered by the repository's ignore rules.\n\n**Result of the audit** (Access Control, Trust Gap, Asymmetry lenses on `IMDBHook` and `IMDBToken`):\n\n- **One low finding, with proof.** The hook's `afterSwap` physically takes IMDB out of the PoolManager mid-swap to burn it. A router that pays its IMDB input before swapping, using the valid v4 order of sync, transfer, swap, then settle, is credited input minus the burned amount and the unlock reverts with `CurrencyNotSettled`. The same router succeeds on an identical hookless pool and succeeds with the hook when it settles after the swap. Uniswap's own V4Router is unaffected, so this is an integration liveness gap, not fund loss. The proof test under `test/scratch/PrepaidRouter.t.sol` fails on the current code for this reason and both control tests pass. Suggested minimal fix: keep bought IMDB as a claim in `afterSwap` and redeem plus burn only on the `processFees` unlock, or skip the take when the manager has that token synced.\n- **One informational note.** The buyback runs at the post-trade spot price with a 2% bound and `processFees` is permissionless, so just-in-time liquidity can be the counterparty fee-free. I could not show a material victim: sandwiching the buyback is unprofitable because the attacker pays 2% plus 1.25% each way while the buyback moves price at most about 2%. The project already documents this residual risk.\n\n**What held.** Every enabled callback and the unlock callback are restricted to the PoolManager. The self-quote is only-self and always reverts. Pool key, fee tier 12500 and spacing 60 are enforced in `beforeInitialize`, and pre-deployment initialization fails on empty code. The hook holds no operator or approval over its ERC-6909 claims, so no actor can redirect fee value. Buy and sell fee branches and their specified versus unspecified return deltas are symmetric and reconcile to zero. The token is unmodified OpenZeppelin ERC-20 with caller-only burn. No liquidity callbacks are enabled, so the factory's fee collect cannot be blocked. The site keeps keys module-private, allows only read-only RPC methods, and signs only through an explicitly chosen EIP-6963 provider after rechecking sender and chain.\n\n**Coverage.** All nine listed entry points have rows, plus rows for `beforeInitialize`, the constructor, two invariants and the site's key boundary. The one `finding` row is `afterSwap`. The Math, Boundary and Economic lenses were only checked where they intersected my area.","treeHash":null,"usage":{"cachedInputTokens":2194186,"inputTokens":514,"model":"claude-fable-5-1","outputTokens":45963,"runtime":"claude","turns":41,"wallClockMs":661336}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"7cb215928f3c9473","findings":[{"citation":"resolved","description":"Merged from audit_flow, write_foundry_tests, audit_permissions and audit_math, which all report the same root cause. afterSwap -> _process -> _burnAvailableTokens physically takes IMDB from the PoolManager while the swapper's unlock is still open. PoolManager.settle() credits balanceOfSelf() - syncedReserves. If the swapper ran sync(IMDB) and transfer(IMDB) before swap(), which v4 allows, and calls settle() after the swap, the hook's take of T IMDB lowers the balance between the sync and the settle. The swapper is credited X - T. One of two things happens: the unlock reverts with CurrencyNotSettled, so this kind of sell is impossible, or a router that pays the remaining debt pays T extra IMDB. That T is exactly hook.totalBurned() and stays in the manager as surplus nobody can account for, so the trader loses it. Buys are unaffected because the hook never takes IMD. Routers that settle after the swap work normally (TestRouter, V4Router SETTLE before SWAP). Fix: never take IMDB inside the swap callbacks. Keep purchased IMDB as an ERC-6909 claim and redeem and burn it only in processFees/unlockCallback. Alternatively, skip the take when TransientStateLibrary.getSyncedCurrency(poolManager) == IMDB.","line":243,"path":"src/IMDBHook.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 {IMDBToken} from \"src/IMDBToken.sol\";\nimport {IMDBHook} from \"src/IMDBHook.sol\";\nimport {HookFlags} from \"src/HookFlags.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 {TransientStateLibrary} from \"v4-core/src/libraries/TransientStateLibrary.sol\";\nimport {TickMath} from \"v4-core/src/libraries/TickMath.sol\";\nimport {PoolKey} from \"v4-core/src/types/PoolKey.sol\";\nimport {Currency} from \"v4-core/src/types/Currency.sol\";\nimport {SwapParams, ModifyLiquidityParams} from \"v4-core/src/types/PoolOperation.sol\";\nimport {BalanceDelta} from \"v4-core/src/types/BalanceDelta.sol\";\n\ncontract MintableERC20 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 Pays its input with the documented sync -> transfer step BEFORE swapping and calls settle()\n/// after it, then settles whatever the manager still says it owes.\ncontract PrepayRouter is IUnlockCallback {\n    using TransientStateLibrary for IPoolManager;\n\n    IPoolManager immutable manager;\n\n    constructor(IPoolManager manager_) {\n        manager = manager_;\n    }\n\n    function addLiquidity(PoolKey memory key, int256 liquidity) external {\n        manager.unlock(abi.encode(uint8(0), key, liquidity, Currency.wrap(address(0)), uint256(0), SwapParams(false, 0, 0)));\n    }\n\n    function prepaySwap(PoolKey memory key, Currency payIn, uint256 prepay, SwapParams memory p) external {\n        manager.unlock(abi.encode(uint8(1), key, int256(0), payIn, prepay, p));\n    }\n\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        (uint8 kind, PoolKey memory key, int256 liquidity, Currency payIn, uint256 prepay, SwapParams memory p) =\n            abi.decode(data, (uint8, PoolKey, int256, Currency, uint256, SwapParams));\n        if (kind == 0) {\n            manager.modifyLiquidity(key, ModifyLiquidityParams(-887220, 887220, liquidity, bytes32(0)), \"\");\n        } else {\n            manager.sync(payIn);\n            payIn.transfer(address(manager), prepay);\n            manager.swap(key, p, \"\");\n            manager.settle();\n        }\n        _net(key.currency0);\n        _net(key.currency1);\n        return \"\";\n    }\n\n    function _net(Currency currency) private {\n        int256 delta = manager.currencyDelta(address(this), currency);\n        if (delta < 0) {\n            manager.sync(currency);\n            currency.transfer(address(manager), uint256(-delta));\n            manager.settle();\n        } else if (delta > 0) {\n            manager.take(currency, address(this), uint256(delta));\n        }\n    }\n}\n\ncontract PrepaidSellerOverpaysTest is Test {\n    address constant IMD = 0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7;\n\n    function test_exactInputSellerPaysOnlyTheSoldAmount() public {\n        IPoolManager manager = IPoolManager(address(new PoolManager(address(this))));\n        vm.etch(IMD, address(new MintableERC20()).code);\n        IMDBToken token = new IMDBToken();\n        bytes32 hash = keccak256(abi.encodePacked(type(IMDBHook).creationCode, abi.encode(manager, token)));\n        IMDBHook hook;\n        for (uint256 i;; ++i) {\n            address predicted = address(\n                uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), bytes32(i), hash))))\n            );\n            if (!HookFlags.matches(predicted, 0x20cc)) continue;\n            hook = new IMDBHook{salt: bytes32(i)}(manager, token);\n            break;\n        }\n        PoolKey memory key = hook.poolKey();\n        manager.initialize(key, 79228162514264337593543950336);\n\n        PrepayRouter lp = new PrepayRouter(manager);\n        token.transfer(address(lp), 10_000_000 ether);\n        MintableERC20(IMD).mint(address(lp), 10_000_000 ether);\n        lp.addLiquidity(key, 1_000_000 ether);\n\n        PrepayRouter seller = new PrepayRouter(manager);\n        token.transfer(address(seller), 1_000 ether);\n        bool zeroForOne = !hook.pairIsCurrency0(); // selling IMDB for IMD\n        uint256 sold = 100 ether;\n        uint256 before = token.balanceOf(address(seller));\n        seller.prepaySwap(\n            key,\n            Currency.wrap(address(token)),\n            sold,\n            SwapParams(zeroForOne, -int256(sold), zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1)\n        );\n        // Expected: an exact-input sell of 100 IMDB costs the seller 100 IMDB.\n        // Actual: the hook's afterSwap takes and burns IMDB from the manager between the seller's sync and\n        // settle, so settle() credits ~98.05 and the seller pays the ~1.95 IMDB the hook burned on top.\n        assertEq(before - token.balanceOf(address(seller)), sold, \"seller funded the hook's buyback burn\");\n    }\n}","reproduction":"Fresh PoolManager. IMDB/IMD pool at sqrtPrice 2^96 with 1e24 full-range liquidity. The seller's unlockCallback runs: sync(IMDB); IMDB.transfer(manager, 100e18); swap(exact-in sell, amountSpecified=-100e18, extreme limit); settle(); then it pays whatever currencyDelta still says it owes and takes the IMD output. Expected: the seller's IMDB balance drops by 100e18. Actual: it drops by 101950501289272329264, and the extra 1950501289272329264 equals hook.totalBurned(). Without the top-up step the unlock reverts with CurrencyNotSettled(). Proof_b1a5e599c1ef has a control: the same flow on a pool with no hook succeeds. Ran both proofs with forge test --match-path: the proof below fails with 'seller funded the hook's buyback burn: 101950501289272329264 != 100000000000000000000', and Proof_b1a5e599c1ef fails with CurrencyNotSettled while its two controls pass.","severity":"medium","snippet":"        poolManager.take(Currency.wrap(address(token)), address(this), amount);","title":"afterSwap takes IMDB out of the PoolManager mid-unlock, so a seller who synced and prepaid IMDB before swap() reverts with CurrencyNotSettled or pays the burned amount twice"},{"citation":"resolved","description":"_process always submits an exact-input swap of the whole pending claim, with a limit 1% (in sqrt price) past spot. It accepts spent == 0 because line 223 reverts only when spent > available. With no liquidity at the current tick, v4 moves sqrtPrice straight to the limit without consuming any input. So each processFees() call moves slot0 by 1% in the buy direction, spends nothing, and leaves pendingIMD unchanged. Repeated calls walk the price into an LP's range far above market, and the hook then spends its accumulated fee IMD there at an inflated price, burning fewer IMDB. Precondition: the launch's full-range position has been removed and no other liquidity lies in between. Fix: skip the buyback when poolManager.getLiquidity(id) == 0, or revert or skip when spent == 0.","line":220,"path":"src/IMDBHook.sol","reproduction":"Pair-first ordering. Seed 1_000_000e18 full-range liquidity. Buy 20_000_000e18 IMD exact-in, which leaves pendingIMD > 0. Remove the full-range position (-1_000_000e18). Call hook.processFees(). Expected: no price change, or a revert. Actual (verified in a scratch Foundry test): sqrtPrice goes from 3853396260826415829899706412 to 3814862298218151671600709347 (exactly x0.99), while totalBuybackIMD and pendingIMD stay the same. Each further call moves the price another 1%.","severity":"low","snippet":"                    poolManager.swap(key, SwapParams(pairIsCurrency0, -available.toInt256(), bound.toUint160()), \"\");","title":"When in-range liquidity is zero, the buyback swap moves the price 1% without spending anything, and permissionless processFees() can walk the price into a far range where pending fees are then spent"}],"hash":"4699692304c40a65e3364311242a310b63123591103e2cced9f171e8f9c386fe","nodeId":"42ffb81d-480c-4f6a-9fda-cef4d55ba696","outcome":"completed","summary":"I kept 2 findings and wrote them to `.imd-findings.json`, with a coverage row for each of the 9 entry points. No tracked files were changed, and I deleted the scratch tests afterwards.\n\n**Kept:**\n\n1. **Medium – IMDB is taken from the PoolManager in the middle of a swap** (`src/IMDBHook.sol:243`). Four specialists reported this same cause, so it is one finding here. Inside `afterSwap`, the hook pulls the IMDB it bought back out of the PoolManager while the seller's transaction is still open. A seller whose router pays its IMDB in before calling `swap()` and settles after it is short by exactly the amount the hook burned. The transaction then either reverts with `CurrencyNotSettled`, or the router covers the gap and the seller overpays: an exact-in sell of 100 IMDB cost 101.95 IMDB, and the extra 1.95 IMDB equals `totalBurned`. Both supplied proofs fail on this code for that reason. One of them includes a control with no hook, which passes. I attached the overpay proof. I rated it medium, not high: buys are unaffected, and so are routers that settle before swapping or settle all at once after the swap, such as Uniswap's own.\n2. **Low – a buyback can move the price without buying anything** (`src/IMDBHook.sol:220`). When the pool has no liquidity at the current price, the buyback swap moves the price 1% and spends nothing. Anyone can call `processFees()` repeatedly to walk the price into an LP's range far above market, where the hook then spends its accumulated fees at an inflated price and burns fewer IMDB. My scratch test confirmed the exact 0.99× move with no spend and the pending fees unchanged. This only happens if the launch's full-range liquidity has been removed.\n\n**Dropped:**\n- **Liquidity added just before `processFees()`** (two specialists). This is how any AMM liquidity provider sells into a trade, and the burn itself is intact, so it is not a hook defect.\n- **No fee on other IMDB pools.** This follows from the brief's plain ERC-20 with no transfer tax.\n- **Fee rounds to zero below 50 wei.** It is documented and too small to exploit.\n\n**My own pass found nothing new:**\n- The fee math works out in all four swap modes (buy and sell, exact-in and exact-out).\n- The trial-swap fee estimate run before each swap matches what the swap actually fills.\n- The hook's balances net to zero on every path.\n- The reentrancy guards hold.\n- `launch.json` is consistent with the code: the address flags (0x20cc), constructor arguments and LP fee 12500 all match.\n\nThe specialists' concern about `beforeInitialize` restricting who creates the pool is also covered: it accepts calls only from the PoolManager and only for this pool's exact settings.","treeHash":null,"usage":{"cachedInputTokens":431218,"inputTokens":14,"model":"claude-opus-5-5","outputTokens":13219,"runtime":"claude","turns":7,"wallClockMs":189370}},{"artifacts":[],"attempt":1,"bundleHash":"414df1950640a20d9c4ecd00271fa4f2688f5cc4993d9aba5f0d91e1db629c0c","device":"52c98c0dc01791cd","findings":[],"hash":"5cb2a792c830c89fa38396ce095a7d1c9d820b5377c7957fe3d590088271d291","nodeId":"4d84d37b-f6e7-4525-913c-52797dc2dade","outcome":"completed","summary":"Done. The only file created is launch.json at the repository root. Nothing else in the tree was changed.\n\n**What the manifest declares**\n\n- Hook contract `IMDBHook` with constructor arguments written as the two placeholders, pool manager then token, matching the declaration order `(IPoolManager manager_, IMDBToken token_)`.\n- Permissions copied from the hook's getHookPermissions: beforeInitialize, beforeSwap, afterSwap, beforeSwapReturnDelta, afterSwapReturnDelta. These correspond to the hook's address flag mask 0x20cc.\n- Token contract `IMDBToken`, name IMDBundler, symbol IMDB, 18 decimals, no constructor arguments.\n- Pool values exactly as the launch facts give them: IMD pair address, fee 12500, tick spacing 60, initial price \"79228162514264337593543950336\".\n- Notes describe the 2% IMD-side fee on settled deltas, the in-swap buyback and burn via ERC-6909 claims, the permissionless processFees retry, the beforeInitialize key check, and the absence of any owner or admin.\n\nNo $owner, $factory or $launchNumber placeholder is needed: the hook has no owner, does not check who initializes the pool, and never calls the factory. No hand-written address appears other than the IMD pair currency.\n\n**Checks run**\n\n- `forge build` succeeded with lint warnings only.\n- `forge test` with the pinned fuzz seed passed 52 of 52 tests across 5 suites, including both invariant campaigns.\n- launch.json parses as JSON, its first key is `\"kind\": \"univ4_hook\"`, and the notes are well under the 4000 character cap.","treeHash":"6426162059f108f4e2338ec28c081324deb87d30","usage":{"cachedInputTokens":361475,"inputTokens":194,"model":"claude-fable-5-1","outputTokens":4304,"runtime":"claude","turns":13,"wallClockMs":78145}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"2ab0b432477fde6c","findings":[{"citation":"resolved","description":"The in-swap buyback is bounded to a 1% sqrt-price move, so a large buy leaves most of its 2% fee as a deferred IMD claim (pendingIMD). Anyone may then call processFees() at a moment of their choosing. Because liquidity callbacks are disabled (by design, so the factory can collect), an IMDB holder can mint single-sided IMDB liquidity in the narrow band the buyback will sweep, call processFees(), and remove the position in the same transaction. The hook's deferred IMD is paid to that position at the pool price, the position collects the 12500-pip LP fee on the buyback, and no hook fee is charged: the holder has converted IMDB to IMD without the 2% the brief requires on every sell, and the launch factory's position loses the LP fee share it would otherwise have earned on that buyback. The IMDB the hook receives is still burned, so the burn mechanism itself is intact; the leak is the avoided 2% (and the factory's LP fee) on the amount of pending claims, which is bounded by pendingIMD at the time of the call. Measured in a Foundry run on a 1:1 pool with 1e6 ether full-range liquidity: a 20,000,000 IMD exact-in buy leaves 191,791 IMD pending (the in-swap buyback executed only ~52% of the 400,000 IMD fee); a JIT position of 50,000,000 liquidity in the 600-tick band adjacent to the price then calls processFees() and withdraws: it gives up 439.15 IMDB and receives 188,030 IMD plus LP fees, totalFeesIMD is unchanged (hook fee charged = 0) and pendingIMD drops to 0. Token-first ordering: 416.26 IMDB for 178,395 IMD, fee 0. Sandwiching processFees() with swaps is not profitable (round trip pays ~6.5% in hook+LP fees against at most ~2% price movement), so this is specifically the liquidity route. No in-hook fix preserves the design: gating liquidity would block the factory's zero-change fee collection the launch requires, and the quantity burned is unchanged. Report as an economic property for the author to accept or document: the 2% sell fee is enforceable on swaps only, and deferred claims are convertible by whoever supplies in-range liquidity.","line":183,"path":"src/IMDBHook.sol","reproduction":"State: pool initialized at sqrtPrice 2^96 with 1,000,000 ether full-range liquidity; pendingIMD()==0. Step 1: router.swap(key, zeroForOne=buy, amountSpecified=-20_000_000 ether, limit=MIN_SQRT_PRICE+1) -> hook.pendingIMD() == 191,791,331,031,837,360,951,284 (pair-first). Step 2: attacker router mints modifyLiquidity(lower=tick/60*60-600, upper=tick/60*60, liquidityDelta=+50_000_000 ether) in the IMDB-only band below the price (pair-first; above the price for token-first). Step 3: attacker calls hook.processFees(). Step 4: attacker modifyLiquidity(..., -50_000_000 ether). Observed: attacker IMDB balance -439,152,791,119,668,374,383; IMD balance +188,030,716,697,879,765,638,512; hook.totalFeesIMD() unchanged; hook.pendingIMD() == 0. Expected under the brief ('2% fee on every sell'): converting 439 IMDB to IMD through this pool charges 2% of the gross IMD (about 3,760 IMD) and that IMD is bought back and burned; actual: 0 fee charged and the LP fee on the 191,791 IMD buyback accrues to the attacker's position instead of the factory's.","severity":"low","snippet":"    function processFees() external {\n        if (swapping || processing) revert Busy();\n        processing = true;\n        if (poolManager.unlock(\"\").length != 0) revert UnexpectedUnlock();\n        processing = false;\n    }","title":"Deferred fee claims let a just-in-time LP exit IMDB without the 2% sell fee and capture the buyback's LP fee via permissionless processFees()"},{"citation":"resolved","description":"IMDBToken is a plain OpenZeppelin ERC-20 with no transfer hook, as the brief requires (immutable, no transfer tax). The 2% fee and buyback-and-burn therefore exist only inside the one PoolKey the hook accepts. Anyone can initialize a hookless v4 pool (or a v2/v3 pool) for IMDB/IMD or IMDB/ETH and trade there; those trades pay no hook fee and fund no burn, and once such a pool has liquidity arbitrage will keep its price in line with the hooked pool so traders can route around the fee entirely. This follows directly from the brief's own constraints (standard ERC-20 plus a hook), so it is not a code defect, but the author should state it in the token-info page and README: 'every buy and every sell' means every swap in the launch pool, not every IMDB trade. Reproduced in Foundry: initializing PoolKey{same currencies, fee 3000, tickSpacing 60, hooks address(0)}, seeding 1e6 ether liquidity and swapping 100 ether each way leaves hook.totalFeesIMD()==0 and hook.totalBurned()==0.","line":250,"path":"src/IMDBHook.sol","reproduction":"Given the hooked pool exists: manager.initialize(PoolKey(currency0=IMD, currency1=IMDB, fee=3000, tickSpacing=60, hooks=address(0)), 2^96); modifyLiquidity(-887220, 887220, +1_000_000 ether); swap(zeroForOne=true, -100 ether); swap(zeroForOne=false, -100 ether). Expected under a literal reading of 'take exactly 2% fee on every buy and every sell': 2% of each swap's IMD side charged and burned. Actual: hook.totalFeesIMD()==0, hook.totalBurned()==0, token.totalSupply()==INITIAL_SUPPLY.","severity":"info","snippet":"            address(key.hooks) != address(this) || key.fee != LP_FEE || key.tickSpacing != TICK_SPACING","title":"2% buy/sell fee and burn are enforced only on the hook's single pool key; any other IMDB pool trades fee-free"}],"hash":"5f4ffc6a5f1e59e5809b19ce7dc03dc60f7de72c29c47a4c5de0a3b0e92ce77c","nodeId":"dfffdc4a-897f-443f-9b5d-63e0746baae1","outcome":"completed","summary":"The review is complete. The findings file is valid JSON, both snippets match the tree exactly, and the working tree is clean apart from the gitignored scratch tests and the findings file itself.\n\n**Outcome:** no critical, high or medium defects in the economic area. The hook's fee accounting is correct and I could not break it.\n\n**What I verified**\n\n- **All four fee paths** (buy/sell × exact-in/exact-out) in both currency orderings charge floor(IMD side / 50) on settled deltas. The self-quote plus `FeeMismatch` identity holds under integer rounding. I proved this by hand and with a 1000-run fuzz using tight price limits, liquidity exhaustion and pre-moved prices. No honest trade reverts.\n- **Delta conservation**: hook claims, buyback, remint and burn-and-take always net to zero. The pending-claim invariant and \"hook holds no ERC-20\" invariant held across every scratch scenario.\n- **Buyback cannot DoS a swap**: the price bound is clamped and strictly past the current price, the amount is nonzero and bounded, and zero-liquidity swaps just remint.\n- **Sandwiching the buyback with swaps is unprofitable**: a round trip costs about 6.5% in hook and LP fees against at most 2% of permitted price movement.\n\n**Findings reported**\n\n1. **Low**: deferred fee claims plus permissionless `processFees` let a just-in-time liquidity provider convert IMDB to IMD with no 2% sell fee and capture the buyback's LP fee. Measured: a 20M IMD whale buy leaves ~191.8k IMD pending, and a JIT position then swaps 439 IMDB for 188k IMD with zero hook fee. The burn quantity is unchanged, so impact is bounded.\n2. **Info**: the fee and burn apply only to the one hooked pool key. A hookless IMDB pool trades fee-free, which follows from the brief's own \"standard ERC-20, no transfer tax\" constraint and should be stated on the token page.\n\n**Coverage**: all nine entry points have rows (eight hold, `processFees` references finding 1), plus five invariant and economic rows. Slither and Aderyn leads were checked and none substantiated beyond the trusted-manager call ordering already documented.","treeHash":null,"usage":{"cachedInputTokens":1978334,"inputTokens":514,"model":"claude-fable-5-1","outputTokens":47579,"runtime":"claude","turns":39,"wallClockMs":718305}},{"artifacts":[],"attempt":1,"bundleHash":"0f5d2f0611400b03d40b9423b92b1d75d27926aadead9493b7040ad344f39c68","device":"96eeb8eb2acdbbf1","findings":[],"hash":"8c562e1eb519f87ee1e088b44dff95f1ac37ad95de249f93c905b5c3610fb4d2","nodeId":"3927e3a5-8ef6-48b3-823a-6401d4ba71c4","outcome":"completed","summary":"I added tests for this revision's two hook changes, and I found no new defect, so there is no `.imd-findings.json`. `forge build` succeeds, and `forge test` with the verifier's fuzz seed passes all 112 tests across 11 suites (it was 98 before), with no skips.\n\nThe two changes are:\n1. The hook no longer burns IMDB while a payer has IMDB \"synced\" for a later settlement.\n2. It no longer runs the buyback when the pool has no liquidity at the current price.\n\n**New file `test/IMDBHookRevision.t.sol`** (7 tests, run for both token orderings):\n- **Prepaid payer fuzz test:** a payer syncs IMDB or IMD, prepays a random amount, swaps in any direction and mode, then settles. It must pay exactly the swap's amounts, the 2% fee rule must hold, and a later `processFees()` must clear all pending IMDB.\n- **Someone else with IMDB synced:** this only delays the burn and gains them nothing. They pay their full budget, the delayed IMDB stays the hook's, and a retry burns exactly that amount.\n- **Two swaps before one settlement:** the payer's outflow equals the two swaps' amounts combined, and the next ordinary swap burns the backlog.\n- **Settling before the swap:** the burn happens immediately, which shows the delay is tied to a live sync, not to prepaying.\n- **Burn with zero liquidity:** the no-liquidity rule only stops the buyback. Delayed burns still go through and the price doesn't move.\n- **Tiny liquidity at the current price:** it re-enables one buyback, which never moves the price beyond the fixed ±1% bound. The next retry then moves nothing.\n- **User swap ending where the pool is empty:** the 2% fee is charged on what was actually settled, the fee waits as pending IMD, and the price stays exactly at the swap's limit.\n\n**Change to my existing `test/HookStatefulInvariant.t.sol`:** I added a `midSyncTrade` action to the random-sequence handler. On every call it checks that the payer's real token flows equal the swap's amounts and that nothing burns while IMDB is synced. It ran about 300 times per campaign with no reverts.\n\n**Check that the tests catch the bugs:** I temporarily removed both guards from `src/IMDBHook.sol`, then restored the file; `src` has no changes. With the guards gone, 6 of the 7 new tests failed, and every invariant failed with \"mid-sync payer IMDB overcharged\". The one test that still passed only checks the user-side behaviour when the burn happens immediately.\n\n**Two notes:**\n- The `forge-config` fuzz-run overrides don't apply to tests inherited from shared abstract contracts in this forge version, so those fuzz tests run the default 256 times.\n- I'm not filing this as a finding: the buyback's ±1% limit is measured from the current price, which a trader can push first. Any loss is capped at that bound on whatever IMD is pending, and I have no concrete losing case.\n\nNothing is committed: the changes are the new test file and the edited invariant file.","treeHash":"59905e92a5e7cf7ffce635a81951e3ec6024566a","usage":{"cachedInputTokens":993059,"inputTokens":28,"model":"claude-opus-5-5","outputTokens":21555,"runtime":"claude","turns":15,"wallClockMs":333706}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"8c9c4986ca881107","findings":[{"citation":"resolved","description":"Boundary: external call poolManager.take during the user's own swap. Assumption: the PoolManager's physical IMDB balance only backs claims and pool reserves. Actual: v4 settlement for ERC-20s is sync -> transfer -> settle and credits `paid = balanceNow - reservesAtSync` (PoolManager._settle). If a router syncs IMDB and transfers the input before calling swap (pre-funded settlement), the hook's take(token, hook, amount) in _burnAvailableTokens executes between that sync and the router's settle and lowers balanceNow. The router is credited X - T instead of X, leaves a -T IMDB delta, and the unlock reverts with CurrencyNotSettled. A router that instead tops up whatever debt remains pays T extra IMDB from the trader; those T tokens become an unclaimable surplus in the manager (the hook already took T against its burned claim), so the trader loses the buyback amount (about 2% of the trade in IMDB terms). Buys are unaffected because the hook never takes IMD. Sell routers that sync after the swap (Uniswap's V4Router/Universal Router settle actions, the repo's TestRouter) are unaffected, so this is an integration hazard rather than a general outage. Mitigation options that keep the design: defer the physical take+burn to a path outside the user's swap (e.g. only in processFees/unlockCallback) or skip the take when a currency is currently synced (`poolManager.getSyncedCurrency()` via TransientStateLibrary equals the token).","line":243,"path":"src/IMDBHook.sol","reproduction":"State: pool seeded with full-range liquidity (router.modify(key,-887220,887220,1_000_000 ether)); IMDB is physically held by the manager. Router R implements unlockCallback as: manager.sync(IMDB); IMDB.transfer(manager, 100e18); manager.swap(key, {zeroForOne: sell direction, amountSpecified: -100e18, limit: far}); manager.settle(); take IMD. Call R.sell(100e18). Expected: sale succeeds, trader receives gross IMD minus 2%. Actual: during afterSwap the hook buys IMDB with the fee and takes T IMDB (T = totalBurned delta, ~2% of trade value); R.settle() credits 100e18 - T; R's IMDB delta is -T and manager.unlock reverts with CurrencyNotSettled. The identical router buying (sync IMD, transfer, swap, settle) succeeds. Reproduced in test/scratch/Probe.t.sol test_preSyncSellerRouterReverts for both currency orderings (vm.expectRevert on the sell, then the buy passes).","severity":"low","snippet":"        poolManager.take(Currency.wrap(address(token)), address(this), amount);","title":"Hook takes IMDB inside afterSwap, so a seller whose router syncs and pre-funds IMDB before the swap reverts (or overpays by the buyback amount)"},{"citation":"resolved","description":"Seam: boundary (zero liquidity) x invariant (a buyback changes price only by buying). v4's swap loop moves sqrtPrice to the limit through ranges with zero liquidity without consuming input. _process always submits an exact-input swap for the whole pending claim with a limit 1% (sqrt) past the current price, and accepts spent == 0 / received == 0 (it only reverts on spent > available). So while the current tick has no liquidity, each call to the permissionless processFees() moves slot0 by exactly 1% in the buy direction, emits Buyback(0,0), spends nothing, and leaves the claims untouched; totalBuybackIMD and pendingIMD do not change while the price does. Repeating it walks the price arbitrarily far toward MAX/MIN until it enters a liquidity range, where the hook then spends the pending IMD at that range's price. An LP who places a position far above market (IMDB expensive) can walk the price into it for gas only and sell IMDB to the hook at that inflated price, extracting the hook's accumulated fees; with no other liquidity the walked price is also the pool's new spot. Precondition: no liquidity between the current price and the attacker's range, i.e. the launch's full-range position must have been withdrawn (README states the factory can remove its liquidity). With the full-range position present the swap always consumes IMD and the bound is reached by a real purchase, so the launch-day state is not affected. A simple guard that keeps the design: skip the buyback when poolManager.getLiquidity(id) == 0, or require spent != 0 (revert/skip) so a zero-spend swap cannot move the price.","line":220,"path":"src/IMDBHook.sol","reproduction":"State (both orderings): seed full-range 1_000_000e18 liquidity at 1:1; buy exact-in 20_000_000e18 IMD so the bounded buyback leaves pendingIMD > 0; remove the full-range position (modify with -1_000_000e18); add 10_000e18 liquidity in a 600-tick range 6000 ticks away on the buy side (IMDB ~35% more expensive than spot). Call hook.processFees() from any EOA. Expected: price unchanged (no liquidity to buy from) or a revert. Actual (pair-first): sqrtPrice 3853396260826415829899706412 -> 3814862298218151671600709347 (exactly x0.99), totalBuybackIMD and pendingIMD unchanged, Buyback(0,0) emitted. Looping processFees(): after 29 calls the price reaches the far range and totalBuybackIMD increases, i.e. the pending fee IMD is spent 35% above the pre-walk price. Token-first ordering: 1628816140457629097633752693379 -> 1645104301862205388610090220312 (x1.01), 29 calls. Reproduced in test/scratch/Probe.t.sol test_processFeesWalksPriceThroughEmptyRange.","severity":"low","snippet":"                    poolManager.swap(key, SwapParams(pairIsCurrency0, -available.toInt256(), bound.toUint160()), \"\");","title":"Buyback swap moves the pool price by 1% with zero spend whenever in-range liquidity is zero; public processFees lets anyone walk the price into a distant range where pending fees are then spent"},{"citation":"resolved","description":"Math Precision: fees should round up; every fee here is floor(base/50) (beforeSwap lines 115/119/123, afterSwap line 142). A buy of 1..49 wei IMD or a sell whose gross IMD output is 1..49 wei pays zero hook fee, and every other trade underpays by up to 49 wei. docs/HOOK.md documents the floor. With 18-decimal IMD the leak is at most 49 wei per swap and splitting a 1 IMD trade into fee-free pieces would need 2e16 swaps, so it is not exploitable; recorded for completeness and as the concrete behaviour of the boundary.","line":115,"path":"src/IMDBHook.sol","reproduction":"Seed full-range liquidity at 1:1. Swap buy exact-in amountSpecified = -49 (wei): hook.totalFeesIMD() == 0 and no burn occurs. Swap sell exact-in 49 wei IMDB: gross IMD out < 50, fee 0. Expected under round-up semantics: 1 wei fee. Reproduced in test/scratch/Probe.t.sol test_dustBuyPaysNoFee for both orderings.","severity":"info","snippet":"                fee = amount / 50;","title":"Fee floors to zero for trades under 50 wei of IMD (documented; fee rounds down rather than up)"}],"hash":"9f4bf963c6cecdd2d07349591fe2e254fb3ac85ebd5a09cff3f6b05f2079de6e","nodeId":"ea6a446c-ac12-4ce7-9de1-01f19f831972","outcome":"completed","summary":"The review is complete and `.imd-findings.json` holds three substantiated findings plus a coverage row for every listed entry point.\n\n**Outcome.** The core 2% fee arithmetic is sound. I traced all four trade shapes (buy/sell, exact-in/exact-out), proved the partial-fill identity `floor((C+F)/50) == F` for `F = floor(C/49)`, showed the real swap input always covers the quoted fill, and fuzzed 1,200 runs across gapped liquidity and arbitrary price limits without a single `FeeMismatch`. Overflow, downcast and clamp paths in the buyback bound also hold. No critical or high defects.\n\n**Findings written:**\n\n- **Low, `src/IMDBHook.sol:243`.** The hook physically takes IMDB inside the user's `afterSwap`. A sell router that syncs and pre-funds IMDB before calling `swap` is credited less at `settle` and reverts with `CurrencyNotSettled`. A router that tops up remaining debt instead would charge the trader the buyback amount extra. Buys and settle-after-swap routers are unaffected. Reproduced for both currency orderings.\n- **Low, `src/IMDBHook.sol:220`.** When no liquidity is in range, the buyback swap moves the pool price exactly 1% with zero spend, and `processFees` is permissionless. With the full-range position withdrawn, 29 calls walked the price into a range 6,000 ticks away, where the hook then spent its pending IMD about 35% above the prior price. Precondition is that the launch's full-range liquidity is gone, so launch-day state is unaffected.\n- **Info, `src/IMDBHook.sol:115`.** Fees floor to zero below 50 wei. Documented, at most 49 wei per swap, not exploitable.\n\n**Coverage.** All nine ABI entry points have rows. Seven hold, `afterSwap` and `processFees` reference findings 1 and 2. Two invariant rows record the quote-equals-fill proof and the bound arithmetic. The site, release artifact and manifest are marked unreached as outside the math area.\n\nScratch tests live in `test/scratch/Probe.t.sol` and are not part of the submission. No repository files were changed.","treeHash":null,"usage":{"cachedInputTokens":1436547,"inputTokens":386,"model":"claude-fable-5-1","outputTokens":49723,"runtime":"claude","turns":28,"wallClockMs":689736}},{"artifacts":[],"attempt":1,"bundleHash":"bdc4563ea58b987171478c28fb93efb716224eaf055a39b56eb2a24d18e37cd5","device":"61b4050710026370","findings":[],"hash":"af68e0923b60647308e2942753a5fd31a62e7a2a1f1750684fd48aebdb3a58be","nodeId":"4d84d37b-f6e7-4525-913c-52797dc2dade","outcome":"completed","summary":"Done. The manifest at `launch.json` is verified against the accepted implementation and is the only file changed.\n\n**What I checked**\n\n- Constructor: the hook takes `(IPoolManager, IMDBToken)`, so the arguments are `$poolManager` and `$token` in that order. No owner or factory parameter exists, so no `$owner` or `$factory` placeholder applies.\n- Permissions: `getHookPermissions` returns beforeInitialize, beforeSwap, afterSwap, beforeSwapReturnDelta and afterSwapReturnDelta. The manifest lists exactly those, and they match the flag mask the hook validates against in its constructor.\n- Token: contract `IMDBToken`, name IMDBundler, symbol IMDB, 18 decimals, no constructor arguments.\n- Pool: paired currency is the IMD address, fee 12500, tick spacing 60, initialPrice as the launch facts give it.\n- Notes: every behavioural claim (IMD-side fee, ERC-6909 claim minting, same-swap buyback and burn, `processFees` retry, public burn counters, disabled liquidity callbacks) matches the current source. Length is well under the 4000 character cap.\n\n**What I changed**\n\nOne sentence was added to the notes stating that no owner or factory placeholder applies, and one describing the client-side site in the tree, since the task asks the manifest to describe what was built. All previously accepted content is untouched.\n\n**Verification**\n\n`forge build` and `forge test` with the verifier's fuzz seed pass: 62 tests, 0 failures, including the invariant suites. `git status` shows only `launch.json` modified.","treeHash":"7dca38cc5982a5fb75793b9f644ed5485ec61084","usage":{"cachedInputTokens":417022,"inputTokens":226,"model":"claude-fable-5-1","outputTokens":5018,"runtime":"claude","turns":13,"wallClockMs":127134}},{"artifacts":[],"attempt":1,"bundleHash":"311564fba574d232ce34d39a3a44f3b157926dc7b2cacb21e7e533e450b8e2b7","device":"264c3bd81028b74a","findings":[],"hash":"beae4f17c2284e4e9e6f1776abaa007ad6c0cbbf8ea5befe95ac6069b828c7e7","nodeId":"ff9c7ffc-711c-4710-b926-3daf1327cb11","outcome":"completed","summary":"Implemented the token, immutable 2% buyback-and-burn hook, client-side wallet site, Pepe P&L cards, and IPFS package.\n\nPassed `forge build`, `forge fmt --check`, 52 Foundry tests, 18 site tests, and four mainnet IMD fork tests.\n\nDeployment, publishing, domain activation, and live addresses remain pending with the launch pipeline. Instructions are in [README.md](/root/.identitymd/work/3e287c7b-b983-4427-a05a-197320b17ccc/ff9c7ffc-711c-4710-b926-3daf1327cb11/README.md).","treeHash":"0f3f727981d4289a65a09774487a1d3051b1df74","usage":{"cachedInputTokens":6909440,"inputTokens":227564,"model":"gpt-6-astra","outputTokens":121921,"runtime":"codex","turns":8,"wallClockMs":4191203}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"8f08088e7a7f557f","findings":[{"citation":"resolved","description":"Execution x periphery seam. _burnAvailableTokens (called from _process inside afterSwap) sizes its take by token.balanceOf(address(poolManager)) at line 238 and then physically transfers that IMDB out of the manager at line 243, while the user's swap is still in flight. PoolManager.settle() credits the payer with balanceOfSelf() minus the reserves recorded at sync(). Any router that uses the legal v4 ordering sync(input) -> transfer(input) -> swap -> settle() therefore sees the hook's take shrink the manager's IMDB balance after its sync and before its settle: for a sell (input IMDB) the hook's take of T tokens makes settle() credit X - T instead of X, the router is left with a -T IMDB delta, and the unlock reverts with CurrencyNotSettled. The same prepaid flow for a buy (input IMD) succeeds because the hook never takes IMD. A router that instead tops up any residual negative delta (e.g. a SETTLE_ALL after a prepaid SETTLE) would make the user pay T extra IMDB that becomes unaccounted manager balance. Uniswap's own routers settle after the swap and are unaffected, so this is an integration restriction specific to IMDB sells on this pool rather than a loss on the standard path. The deferral logic already handles the manager owing IMDB (test_tokenBurnDefersUntilSellerSettles); minimal fix options are to redeem purchased IMDB claims outside the user's swap (e.g. only in processFees), or to cap the take by the synced reserves when the synced currency is IMDB (poolManager.getSyncedCurrency()/getSyncedReserves()) so an in-flight prepayment is never consumed.","line":243,"path":"src/IMDBHook.sol","reproduction":"Setup: fresh PoolManager, IMDBToken, IMDBHook mined at flags 0x20cc, pool initialized at sqrtPrice 2^96, full-range liquidity 1_000_000e18 (as in test/IMDBHook.t.sol HookFixture + seed()). Router R holding 1_000e18 IMDB and 1_000e18 IMD executes inside unlock: sync(IMDB); IMDB.transfer(manager, 100e18); swap(key, SwapParams(zeroForOne = !pairIsCurrency0, amountSpecified = -100e18, limit = MIN/MAX bound), ''); settle(); take(IMD, R, out). Expected: a standard exact-in sell of 100 IMDB with 2% IMD fee. Actual: afterSwap buys IMDB with the fee claim and takes about 1.9e18 IMDB from the manager at line 243, settle() credits only 100e18 - 1.9e18, and unlock reverts with IPoolManager.CurrencyNotSettled(). The identical prepaid sequence with input IMD (a buy of 100e18 IMD) completes and burns IMDB. Verified in both currency orderings with test/scratch/Review.t.sol::test_prepaidSellRevertsButPrepaidBuyWorks (passes, i.e. the revert is reproduced).","severity":"low","snippet":"        poolManager.take(Currency.wrap(address(token)), address(this), amount);","title":"afterSwap takes IMDB from the PoolManager between a router's sync and settle, so a router that pays its input before swapping cannot sell IMDB"}],"hash":"d8ed774f41b3e3d90db419d71e26f63c2cc64b2351b431cd3740c0c602c53754","nodeId":"6ca072e5-c851-4eb8-8d1a-bd31c3b6df56","outcome":"completed","summary":"Findings file validated: one low finding with an exact snippet match, 13 coverage rows covering all 9 verifier entry points plus checked invariants. The scratch test lives only under test/scratch, and the tree is otherwise untouched.\n\n**Result: one low-severity finding, no critical/high/medium defects in the hook's control flow.**\n\nScope covered in depth (Execution Trace, Periphery, First Principles guides, plus the senior-auditor mindset): every path through `beforeSwap`, `afterSwap`, `quoteSwap`, `processFees`, `unlockCallback`, `beforeInitialize`, the `_process` buyback and `_burnAvailableTokens`, `HookBase`, `HookFlags`, `IMDBToken`, and the v4-core primitives they rely on. I verified the vendored v4-core skips this hook's callbacks for hook-initiated swaps, which the self-quote and buyback depend on, and confirmed the mainnet IMD pair is an OpenZeppelin-based LayerZero OFT with plain transfer semantics. The fee arithmetic for all four buy/sell exact-in/out cases, including partial fills, checks out with the quoted fill always equal to the real fill. The project's 52 tests pass.\n\n**Finding (low), `src/IMDBHook.sol:243`.** Inside `afterSwap`, the hook physically takes purchased IMDB out of the PoolManager. A router that pays its input before swapping (sync, transfer, swap, settle) has its IMDB prepayment partly consumed by that take, so `settle()` undercounts and the unlock reverts with `CurrencyNotSettled`. The same prepaid flow for a buy works. Uniswap's own routers settle after the swap, so the standard path is unaffected. Reproduced in both currency orderings with a scratch test. Suggested fix: redeem IMDB claims only in `processFees`, or cap the take by the manager's synced reserves when IMDB is the synced currency.\n\nNot reported after testing: repeated permissionless `processFees` calls do spend all pending fees despite the per-call 1% bound, but pending is only about 2% of post-trade reserves, so cumulative impact is dust and matches the documented design. Static-analysis leads were all benign on inspection.","treeHash":null,"usage":{"cachedInputTokens":1593707,"inputTokens":290,"model":"claude-fable-5-1","outputTokens":57951,"runtime":"claude","turns":38,"wallClockMs":875191}},{"artifacts":[],"attempt":1,"bundleHash":"545087f23a9f2fea28ff8f6352d15e20875eb2aa2930359fb56edca2296f59d0","device":"d63ea36a2b809080","findings":[],"hash":"df54acfb9b8fc68a8b0ab43d6a2ad8fa65d1e2ec73aa4eb1b2976cb095f48295","nodeId":"ff9c7ffc-711c-4710-b926-3daf1327cb11","outcome":"completed","summary":"Both findings reproduced and are fixed. The hook diff is two guarded early-exits; nothing else in the accepted implementation changed.\n\n**Finding 841aea96 (medium, with proof): fixed.** The proof failed on the starting tree exactly as reported, with the seller overpaying by the burned amount. The cause was the burn step taking IMDB out of the PoolManager during the swapper's open sync. The hook now checks the manager's synced currency before taking, and defers the burn as a claim when a payer has IMDB synced. The proof passes. Regression tests for both currency orderings cover the prepaying seller (fixed and fuzz, exact-in and exact-out), plus two controls that still burn in flow: a seller who settles before swapping, and a buyer with IMD synced. The seller tests fail on the old hook.\n\n**Finding 03615ec5 (low, advisory): fixed.** Running the new test against the old hook confirmed the free price walk: three retries moved the square-root price by exactly 0.99 cubed with nothing spent. The buyback is now skipped when the pool has zero liquidity at the current tick. The test checks that repeated retries leave the price and pending fees untouched, and that a retry spends once liquidity returns.\n\nChecks on the delivered tree:\n\n| Check | Result |\n| --- | --- |\n| forge build, forge fmt --check | pass |\n| forge test with the pinned fuzz seed | 62 passed, 0 failed |\n| Reviewer proof copied under test/scratch | passes on the fix, failed before |\n| node site tests | 18 passed |\n\nFiles changed: `src/IMDBHook.sol`, `test/IMDBHook.t.sol`, `test/mocks/TestRouter.sol` (new prepaying swap mode), and the docs in `docs/HOOK.md`, `docs/SECURITY.md` and the README. Both responses are recorded in `.imd-responses.json`. One residual is documented rather than changed: a buyback that starts in range and runs off its end still moves to its bound, which is the same cost-free move v4 allows any swapper through an empty range.","treeHash":"b55a25aaea074dc4969045503691a1c4e50153e1","usage":{"cachedInputTokens":1790280,"inputTokens":642,"model":"claude-fable-5-1","outputTokens":23946,"runtime":"claude","turns":40,"wallClockMs":348296}},{"artifacts":[],"attempt":1,"bundleHash":"598fa07bb82ab94819f111267252b6e25b33870bef49031801c3bef376ead8cc","device":"764cd99b4c628a47","findings":[{"description":"afterSwap -> _process -> _burnAvailableTokens calls poolManager.take(IMDB, hook, amount) and burns the tokens while the swapper's unlock is still open. PoolManager.settle() credits the caller with balanceOf(manager) minus the reserves recorded at sync(). If the swapper (or an earlier step in the same unlock) called sync(IMDB) and transferred IMDB before the swap, as v4 permits, then settle() after the swap sees the balance lowered by the hook's take and credits less than was paid. The swapper must pay the burned amount a second time (or the unlock reverts with CurrencyNotSettled). On an exact-input sell of 100 IMDB the seller pays 101.95 IMDB; the extra 1.95 IMDB is exactly the hook's totalBurned. The loss falls on traders and integrators, not the hook, and the hook's own accounting stays consistent, so the existing invariants do not catch it. Possible fixes: do not take or burn IMDB inside swap callbacks (leave it as a claim and burn it in processFees, which opens its own unlock); or take only when the synced currency is not IMDB (TransientStateLibrary.getSyncedCurrency) and otherwise defer.","line":243,"path":"src/IMDBHook.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 {IMDBToken} from \"src/IMDBToken.sol\";\nimport {IMDBHook} from \"src/IMDBHook.sol\";\nimport {HookFlags} from \"src/HookFlags.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 {TransientStateLibrary} from \"v4-core/src/libraries/TransientStateLibrary.sol\";\nimport {TickMath} from \"v4-core/src/libraries/TickMath.sol\";\nimport {PoolKey} from \"v4-core/src/types/PoolKey.sol\";\nimport {Currency} from \"v4-core/src/types/Currency.sol\";\nimport {SwapParams, ModifyLiquidityParams} from \"v4-core/src/types/PoolOperation.sol\";\nimport {BalanceDelta} from \"v4-core/src/types/BalanceDelta.sol\";\n\ncontract MintableERC20 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 Pays its input with the documented sync -> transfer step BEFORE swapping and calls settle()\n/// after it, then settles whatever the manager still says it owes.\ncontract PrepayRouter is IUnlockCallback {\n    using TransientStateLibrary for IPoolManager;\n\n    IPoolManager immutable manager;\n\n    constructor(IPoolManager manager_) {\n        manager = manager_;\n    }\n\n    function addLiquidity(PoolKey memory key, int256 liquidity) external {\n        manager.unlock(abi.encode(uint8(0), key, liquidity, Currency.wrap(address(0)), uint256(0), SwapParams(false, 0, 0)));\n    }\n\n    function prepaySwap(PoolKey memory key, Currency payIn, uint256 prepay, SwapParams memory p) external {\n        manager.unlock(abi.encode(uint8(1), key, int256(0), payIn, prepay, p));\n    }\n\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        (uint8 kind, PoolKey memory key, int256 liquidity, Currency payIn, uint256 prepay, SwapParams memory p) =\n            abi.decode(data, (uint8, PoolKey, int256, Currency, uint256, SwapParams));\n        if (kind == 0) {\n            manager.modifyLiquidity(key, ModifyLiquidityParams(-887220, 887220, liquidity, bytes32(0)), \"\");\n        } else {\n            manager.sync(payIn);\n            payIn.transfer(address(manager), prepay);\n            manager.swap(key, p, \"\");\n            manager.settle();\n        }\n        _net(key.currency0);\n        _net(key.currency1);\n        return \"\";\n    }\n\n    function _net(Currency currency) private {\n        int256 delta = manager.currencyDelta(address(this), currency);\n        if (delta < 0) {\n            manager.sync(currency);\n            currency.transfer(address(manager), uint256(-delta));\n            manager.settle();\n        } else if (delta > 0) {\n            manager.take(currency, address(this), uint256(delta));\n        }\n    }\n}\n\ncontract PrepaidSellerOverpaysTest is Test {\n    address constant IMD = 0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7;\n\n    function test_exactInputSellerPaysOnlyTheSoldAmount() public {\n        IPoolManager manager = IPoolManager(address(new PoolManager(address(this))));\n        vm.etch(IMD, address(new MintableERC20()).code);\n        IMDBToken token = new IMDBToken();\n        bytes32 hash = keccak256(abi.encodePacked(type(IMDBHook).creationCode, abi.encode(manager, token)));\n        IMDBHook hook;\n        for (uint256 i;; ++i) {\n            address predicted = address(\n                uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), bytes32(i), hash))))\n            );\n            if (!HookFlags.matches(predicted, 0x20cc)) continue;\n            hook = new IMDBHook{salt: bytes32(i)}(manager, token);\n            break;\n        }\n        PoolKey memory key = hook.poolKey();\n        manager.initialize(key, 79228162514264337593543950336);\n\n        PrepayRouter lp = new PrepayRouter(manager);\n        token.transfer(address(lp), 10_000_000 ether);\n        MintableERC20(IMD).mint(address(lp), 10_000_000 ether);\n        lp.addLiquidity(key, 1_000_000 ether);\n\n        PrepayRouter seller = new PrepayRouter(manager);\n        token.transfer(address(seller), 1_000 ether);\n        bool zeroForOne = !hook.pairIsCurrency0(); // selling IMDB for IMD\n        uint256 sold = 100 ether;\n        uint256 before = token.balanceOf(address(seller));\n        seller.prepaySwap(\n            key,\n            Currency.wrap(address(token)),\n            sold,\n            SwapParams(zeroForOne, -int256(sold), zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1)\n        );\n        // Expected: an exact-input sell of 100 IMDB costs the seller 100 IMDB.\n        // Actual: the hook's afterSwap takes and burns IMDB from the manager between the seller's sync and\n        // settle, so settle() credits ~98.05 and the seller pays the ~1.95 IMDB the hook burned on top.\n        assertEq(before - token.balanceOf(address(seller)), sold, \"seller funded the hook's buyback burn\");\n    }\n}","reproduction":"Fresh PoolManager, IMDB/IMD pool at 1:1 with 1e24 full-range liquidity. The seller's unlockCallback runs sync(IMDB), IMDB.transfer(manager, 100e18), swap(exact-input sell of 100e18 IMDB, limit at the extreme), settle(), then pays whatever currencyDelta still says it owes. Expected: the seller's IMDB balance falls by 100e18. Actual: it falls by 101950501289272329264 (the extra 1950501289272329264 equals hook.totalBurned()). Run: forge test --match-path <proof file>.","severity":"medium","title":"Hook takes IMDB from the PoolManager inside afterSwap, so a trader who synced IMDB before the swap pays for the hook's burn"}],"hash":"f24803d32ec05ae984379a1f7e5c8a8e757369cbfd4dfa0e5ec2efaebda757e0","nodeId":"3927e3a5-8ef6-48b3-823a-6401d4ba71c4","outcome":"completed","summary":"I added 36 tests, and they found one real defect in the hook, which I've reported rather than tested around. The full suite passes: 88 tests (52 that were already there plus my 36) with the verifier's fuzz seed, and `forge build` is clean.\n\n## The defect (medium): sellers can pay for the hook's burn\nWhen the hook buys back IMDB after a swap, it pulls those tokens out of the PoolManager with `take` and burns them, while the trader's transaction is still open (`src/IMDBHook.sol:243`). Uniswap v4 lets a trader pay in a different order: record the manager's balance (`sync`), send the tokens, swap, and only then confirm payment (`settle`). In that order the manager sees its balance lowered by the hook's withdrawal, so it credits the trader less than they sent.\n\n- **Reproduction:** an exact-input sell of 100 IMDB paid this way costs the seller 101.95 IMDB.\n- **The extra 1.95 IMDB** is exactly the amount the hook burned.\n- **A stricter router** would revert instead of paying twice.\n- **Who is affected:** traders and integrators who settle this way. The common routers (the periphery's settle helper, Universal Router) sync, pay and settle in one step, so they don't hit it. The hook's own accounting stays consistent, which is why the existing invariants didn't catch it.\n\nThe finding is in `.imd-findings.json` with a self-contained proof test that fails on the current code. It suggests two fixes:\n- leave the bought IMDB as a claim during swaps and burn it in `processFees`, which opens its own unlock; or\n- skip the take while IMDB is the currency someone has synced.\n\n## What the new tests cover\n- **`test/IMDBHookEdges.t.sol`** (17 tests, run with IMD as both currency0 and currency1):\n  - Fuzz tests check that the fee is exactly 2% under random price limits (forcing partial fills), with Uniswap's protocol fee switched on.\n  - They also check that an exact-output sell delivers exactly the requested net amount, and an exact-input buy never takes more than its budget.\n  - Pinned tests check the rounding at 1, 49, 50, 99, 100 and 149 wei.\n  - Failure paths: wrong pool keys rejected even when the PoolManager calls, the reentry guards, the 2¹²⁶ amount limit, and disabled callbacks reverting. Also `processFees` reverting inside another unlock and a buyback skipped at the extreme price without reverting.\n  - Two more: claims donated to the hook are bought back and burned, never withdrawable, and four swaps in one unlock are each charged.\n- **`test/HookStatefulInvariant.t.sol`** runs random call sequences: three traders, a liquidity provider adding and removing positions, batched swaps, claim donations, and `processFees` from random callers. It checks after every sequence that:\n  - supply only falls by the hook's burns;\n  - IMD claims are conserved;\n  - the manager holds enough to cover the hook's claims;\n  - the hook never holds tokens directly, and its counters never go down.\n  \n  At the end every liquidity provider withdraws and processing still works. 2,048 calls per ordering, zero reverts.\n- **`test/mocks/BatchRouter.sol`** is a test router that runs several PoolManager operations inside one unlock.\n\nApart from this defect, the hook's 2% fee math held across all four swap modes and partial fills. Nothing is committed; the new files are untracked in `test/`.","treeHash":"a4b826a8ee7ee61e3df1e59ae538a070d115997b","usage":{"cachedInputTokens":2023179,"inputTokens":44,"model":"claude-opus-5-5","outputTokens":48847,"runtime":"claude","turns":23,"wallClockMs":526105}}],"verification":[{"checks":[{"durationMs":4361,"exitCode":0,"name":"build","output":"Compiling 82 files with Solc 0.8.26\nSolc 0.8.26 finished in 4.21s\nCompiler run successful!\n","passed":true},{"durationMs":1189,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 4 tests for test/IMDBToken.t.sol:IMDBTokenTest\n[PASS] testFuzz_transferAndBurn(uint256) (runs: 256, μ: 134037, ~: 134011)\nLogs:\n  Bound result 353611934014534643903332441\n\n[PASS] test_allowancesCannotStealOrBurnOthers() (gas: 223707)\n[PASS] test_invalidTransferAndAdminSelectorsFail() (gas: 232003)\n[PASS] test_supplyAndMetadata() (gas: 52806)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 8.06ms (8.45ms CPU time)\n\nRan 23 tests for test/IMDBHook.t.sol:TokenFirstHookTest\n[PASS] testFuzz_dustRounding(uint16,bool,bool) (runs: 256, μ: 708115, ~: 727277)\nLogs:\n  Bound result 14\n\n[PASS] testFuzz_feesOnSettledDeltas(bool,bool,uint96,bool) (runs: 256, μ: 823354, ~: 835736)\nLogs:\n  Bound result 1000000000000000000003\n\n[PASS] test_boundedBuybackRetainsAndRetriesClaims() (gas: 1110102)\n[PASS] test_buyExactInput() (gas: 831758)\n[PASS] test_buyExactOutput() (gas: 776061)\n[PASS] test_everyEnabledCallbackRejectsForgedCallers() (gas: 167837)\n[PASS] test_factoryCanCollectAndRemoveLiquidity() (gas: 1003843)\n[PASS] test_failedSettlementRollsBackFeeAndBurn() (gas: 1824342)\n[PASS] test_freshTokenOnlyManager() (gas: 771684)\n[PASS] test_invalidConstructorParametersAndWrongAddressFlags() (gas: 6083)\n[PASS] test_invalidLimitAndExcessiveAmountRevert() (gas: 577379)\n[PASS] test_noAdministrativeEscapeHatches() (gas: 75208)\n[PASS] test_noLiquidityChargesNoFee() (gas: 955558)\n[PASS] test_partialFillBuyExactInput() (gas: 832029)\n[PASS] test_partialFillBuyExactOutput() (gas: 775727)\n[PASS] test_partialFillSellExactInput() (gas: 798512)\n[PASS] test_partialFillSellExactOutput() (gas: 858152)\n[PASS] test_permissionsAndInitialization() (gas: 128494)\n[PASS] test_reentrantProcessingAndSwapCallbacksAreBlocked() (gas: 125867)\n[PASS] test_runtimeContainsNoUpgradeOrDestructionOpcodes() (gas: 3895158)\n[PASS] test_sellExactInput() (gas: 798395)\n[PASS] test_sellExactOutput() (gas: 858157)\n[PASS] test_tokenBurnDefersUntilSellerSettles() (gas: 969129)\nSuite result: ok. 23 passed; 0 failed; 0 skipped; finished in 116.72ms (192.72ms CPU time)\n\nRan 23 tests for test/IMDBHook.t.sol:PairFirstHookTest\n[PASS] testFuzz_dustRounding(uint16,bool,bool) (runs: 256, μ: 708839, ~: 731619)\nLogs:\n  Bound result 1\n\n[PASS] testFuzz_feesOnSettledDeltas(bool,bool,uint96,bool) (runs: 256, μ: 824043, ~: 846576)\nLogs:\n  Bound result 1000000000000000000003\n\n[PASS] test_boundedBuybackRetainsAndRetriesClaims() (gas: 1122156)\n[PASS] test_buyExactInput() (gas: 842567)\n[PASS] test_buyExactOutput() (gas: 780629)\n[PASS] test_everyEnabledCallbackRejectsForgedCallers() (gas: 167307)\n[PASS] test_factoryCanCollectAndRemoveLiquidity() (gas: 1014652)\n[PASS] test_failedSettlementRollsBackFeeAndBurn() (gas: 1810038)\n[PASS] test_freshTokenOnlyManager() (gas: 791252)\n[PASS] test_invalidConstructorParametersAndWrongAddressFlags() (gas: 6083)\n[PASS] test_invalidLimitAndExcessiveAmountRevert() (gas: 577164)\n[PASS] test_noAdministrativeEscapeHatches() (gas: 75208)\n[PASS] test_noLiquidityChargesNoFee() (gas: 968650)\n[PASS] test_partialFillBuyExactInput() (gas: 842204)\n[PASS] test_partialFillBuyExactOutput() (gas: 780988)\n[PASS] test_partialFillSellExactInput() (gas: 794225)\n[PASS] test_partialFillSellExactOutput() (gas: 849250)\n[PASS] test_permissionsAndInitialization() (gas: 128474)\n[PASS] test_reentrantProcessingAndSwapCallbacksAreBlocked() (gas: 125739)\n[PASS] test_runtimeContainsNoUpgradeOrDestructionOpcodes() (gas: 3895158)\n[PASS] test_sellExactInput() (gas: 793865)\n[PASS] test_sellExactOutput() (gas: 850498)\n[PASS] test_tokenBurnDefersUntilSellerSettles() (gas: 957899)\nSuite result: ok. 23 passed; 0 failed; 0 skipped; finished in 116.80ms (187.91ms CPU time)\n\nRan 1 test for test/HookInvariant.t.sol:TokenFirstInvariantTest\n[PASS]\nTokenFirstInvariantTest invariants:\n[PASS] invariant_feesConservedAndBurnsMatchSupply\n[PASS] invariant_noTaxOrAdminBalancesAccumulate\n TokenFirstInvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭--------------+----------+-------+---------+----------╮\n| Contract     | Selector | Calls | Reverts | Discards |\n+======================================================+\n| TradeHandler | retry    | 1015  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| TradeHandler | trade    | 1033  | 0       | 0        |\n╰--------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 3997980755\n  Bound result 195584593970980344\n  Bound result 14239\n  Bound result 2956\n  Bound result 3434\n  Bound result 3069270964\n  Bound result 963004161583187515359\n  Bound result 10916297646815844\n  Bound result 2548616526\n  Bound result 9715\n  Bound result 2498\n  Bound result 369559835174105\n  Bound result 15296\n  Bound result 4\n  Bound result 613\n  Bound result 3105\n  Bound result 19533\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 969.77ms (929.96ms CPU time)\n\nRan 1 test for test/HookInvariant.t.sol:PairFirstInvariantTest\n[PASS]\nPairFirstInvariantTest invariants:\n[PASS] invariant_feesConservedAndBurnsMatchSupply\n[PASS] invariant_noTaxOrAdminBalancesAccumulate\n PairFirstInvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭--------------+----------+-------+---------+----------╮\n| Contract     | Selector | Calls | Reverts | Discards |\n+======================================================+\n| TradeHandler | retry    | 1015  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| TradeHandler | trade    | 1033  | 0       | 0        |\n╰--------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 9694\n  Bound result 195584593970980344\n  Bound result 12358\n  Bound result 4018\n  Bound result 2908\n  Bound result 3069270964\n  Bound result 1896\n  Bound result 10916297646815844\n  Bound result 8570\n  Bound result 1200\n  Bound result 461\n  Bound result 369559835174105\n  Bound result 11589\n  Bound result 4\n  Bound result 6942\n  Bound result 579639759045378835843\n  Bound result 5588\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.02s (988.03ms CPU time)\n\nRan 5 test suites in 1.03s (2.24s CPU time): 52 tests passed, 0 failed, 0 skipped (52 total tests)\n","passed":true},{"durationMs":126,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IMDBHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"IMDBHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"IMDBHook.processFees()\",\"IMDBHook.quoteSwap((address,address,uint24,int24,address),(bool,int256,uint160))\",\"IMDBHook.unlockCallback(bytes)\",\"IMDBToken.approve(address,uint256)\",\"IMDBToken.burn(uint256)\",\"IMDBToken.transfer(address,uint256)\",\"IMDBToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":5,\"LICENSE\":21,\"README.md\":89,\"checks/MainnetPairFork.sol.example\":53,\"docs/HOOK.md\":68,\"docs/OPERATIONS.md\":81,\"docs/SECURITY.md\":86,\"docs/dependencies.json\":152,\"foundry.toml\":22,\"launch.json\":30,\"package.json\":11,\"release/checksums.txt\":1,\"release/cid.txt\":1,\"release/imdbundler.car\":1040,\"remappings.txt\":4,\"scripts/configure-site.mjs\":10,\"scripts/draw-frogs.py\":43,\"scripts/mine-hook.mjs\":23,\"scripts/package-site.mjs\":72,\"scripts/serve.mjs\":19,\"site/LICENSE.txt\":21,\"site/_headers\":6,\"site/app.js\":369,\"site/assets/LICENSE.txt\":6,\"site/assets/pepe-dancing.svg\":7,\"site/assets/pepe-focused.svg\":7,\"site/assets/pepe-happy.svg\":7,\"site/assets/pepe-sad.svg\":7,\"site/assets/pepe-sleepy.svg\":7,\"site/assets/pepe-smug.svg\":7,\"site/assets/pepe-sunglasses.svg\":7,\"site/assets/pepe-thinking.svg\":7,\"site/chains.js\":12,\"site/deployment.json\":4,\"site/index.html\":122,\"site/rpc.js\":54,\"site/style.css\":8,\"site/transactions.js\":75,\"site/vault.js\":98,\"site/vendor/ethers.LICENSE.md\":21,\"site/vendor/ethers.NOTICES.txt\":145,\"site/vendor/ethers.js\":1,\"src/HookBase.sol\":85,\"src/HookFlags.sol\":29,\"src/IMDBHook.sol\":260,\"src/IMDBToken.sol\":18,\"test/HookInvariant.t.sol\":87,\"test/IMDBHook.t.sol\":350,\"test/IMDBToken.t.sol\":70,\"test/mocks/MockERC20.sol\":13,\"test/mocks/ReentryProbeManager.sol\":33,\"test/mocks/TestRouter.sol\":62,\"test/site/core.test.mjs\":164,\"test/site/dom.test.mjs\":100,\"test/site/release.test.mjs\":96,\"test/vendor/linkedom.LICENSE\":15,\"test/vendor/linkedom.NOTICES.txt\":248,\"test/vendor/linkedom.js\":12761},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"5cb2a792c830c89fa38396ce095a7d1c9d820b5377c7957fe3d590088271d291","verifiedTreeHash":"6426162059f108f4e2338ec28c081324deb87d30","verifierVersion":"0.1.0+6a374fcd"},{"checks":[{"durationMs":6014,"exitCode":0,"name":"build","output":"Compiling 86 files with Solc 0.8.26\nSolc 0.8.26 finished in 5.87s\nCompiler run successful!\n","passed":true},{"durationMs":1191,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 4 tests for test/IMDBToken.t.sol:IMDBTokenTest\n[PASS] testFuzz_transferAndBurn(uint256) (runs: 256, μ: 133953, ~: 134011)\nLogs:\n  Bound result 353611934014534643903332441\n\n[PASS] test_allowancesCannotStealOrBurnOthers() (gas: 223707)\n[PASS] test_invalidTransferAndAdminSelectorsFail() (gas: 232003)\n[PASS] test_supplyAndMetadata() (gas: 52806)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 4.77ms (5.05ms CPU time)\n\nRan 7 tests for test/IMDBHookRevision.t.sol:TokenFirstHookRevisionTest\n[PASS] testFuzz_midSyncPayerPaysExactlyTheSwapDeltas(bool,bool,bool,uint256,uint256) (runs: 256, μ: 1057525, ~: 1074083)\nLogs:\n  Bound result 4175\n  Bound result 6000\n\n[PASS] test_buyerWithTokenSyncedDefersBurnWithoutGain() (gas: 1145668)\n[PASS] test_deferredBurnStillRunsWithNoLiquidity() (gas: 1053468)\n[PASS] test_dustInRangeLiquidityBuybackStaysWithinBound() (gas: 1683522)\n[PASS] test_settleBeforeSwapClearsSyncAndBurnsInFlow() (gas: 826138)\n[PASS] test_twoSwapsInsideOneTokenSyncWindow() (gas: 1365509)\n[PASS] test_userSwapEndingInEmptyRangeKeepsFeePending() (gas: 828747)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 137.56ms (144.00ms CPU time)\n\nRan 17 tests for test/IMDBHookEdges.t.sol:TokenFirstHookEdgeTest\n[PASS] testFuzz_exactInputBuyNeverDebitsMoreThanBudget(uint256) (runs: 256, μ: 791047, ~: 806884)\nLogs:\n  Bound result 1486\n\n[PASS] testFuzz_exactOutputSellDeliversRequestedNet(uint256) (runs: 256, μ: 817434, ~: 833136)\nLogs:\n  Bound result 1486\n\n[PASS] testFuzz_protocolFeeDoesNotBreakFeeAccounting(bool,bool,uint256,uint256) (runs: 256, μ: 924095, ~: 911035)\nLogs:\n  Bound result 299999000000000922199200\n  Bound result 286006\n\n[PASS] testFuzz_randomPriceLimitsNeverMischargeOrRevert(bool,bool,uint256,uint256) (runs: 256, μ: 808595, ~: 806094)\nLogs:\n  Bound result 3606363795772736427103226\n  Bound result 3677\n\n[PASS] test_buybackSkippedAtTerminalPriceRetainsClaimWithoutReverting() (gas: 1475121)\n[PASS] test_callbackPhaseGuards() (gas: 250013)\n[PASS] test_disabledCallbacksRevertEvenForManager() (gas: 125848)\n[PASS] test_donateToPoolSkipsHookAndChargesNothing() (gas: 453113)\n[PASS] test_donatedImdClaimsAreBoughtBackAndBurnedNotWithdrawable() (gas: 687207)\n[PASS] test_exactInputBuyRoundingBoundaries() (gas: 1877240)\n[PASS] test_freshManagerTokenOnlyPoolExactOutputBuy() (gas: 700844)\n[PASS] test_hookPinsTheLaunchConfiguration() (gas: 66055)\n[PASS] test_processFeesInsideAnotherUnlockReverts() (gas: 912270)\n[PASS] test_processFeesWithNothingPendingIsANoOpForAnyone() (gas: 500296)\n[PASS] test_specifiedAmountBounds() (gas: 225289)\n[PASS] test_twoOppositeSwapsInOneUnlockAreEachCharged() (gas: 1216098)\n[PASS] test_wrongPoolKeyRejectedEvenFromManager() (gas: 147868)\nSuite result: ok. 17 passed; 0 failed; 0 skipped; finished in 165.29ms (582.49ms CPU time)\n\nRan 17 tests for test/IMDBHookEdges.t.sol:PairFirstHookEdgeTest\n[PASS] testFuzz_exactInputBuyNeverDebitsMoreThanBudget(uint256) (runs: 256, μ: 802126, ~: 818169)\nLogs:\n  Bound result 299\n\n[PASS] testFuzz_exactOutputSellDeliversRequestedNet(uint256) (runs: 256, μ: 809252, ~: 825652)\nLogs:\n  Bound result 299\n\n[PASS] testFuzz_protocolFeeDoesNotBreakFeeAccounting(bool,bool,uint256,uint256) (runs: 256, μ: 923952, ~: 904924)\nLogs:\n  Bound result 6375795772736427103214\n  Bound result 508565\n\n[PASS] testFuzz_randomPriceLimitsNeverMischargeOrRevert(bool,bool,uint256,uint256) (runs: 256, μ: 808554, ~: 810702)\nLogs:\n  Bound result 11654506603753121813732\n  Bound result 6204\n\n[PASS] test_buybackSkippedAtTerminalPriceRetainsClaimWithoutReverting() (gas: 1494623)\n[PASS] test_callbackPhaseGuards() (gas: 250749)\n[PASS] test_disabledCallbacksRevertEvenForManager() (gas: 125848)\n[PASS] test_donateToPoolSkipsHookAndChargesNothing() (gas: 453077)\n[PASS] test_donatedImdClaimsAreBoughtBackAndBurnedNotWithdrawable() (gas: 692566)\n[PASS] test_exactInputBuyRoundingBoundaries() (gas: 1901386)\n[PASS] test_freshManagerTokenOnlyPoolExactOutputBuy() (gas: 712126)\n[PASS] test_hookPinsTheLaunchConfiguration() (gas: 66035)\n[PASS] test_processFeesInsideAnotherUnlockReverts() (gas: 924430)\n[PASS] test_processFeesWithNothingPendingIsANoOpForAnyone() (gas: 500268)\n[PASS] test_specifiedAmountBounds() (gas: 225547)\n[PASS] test_twoOppositeSwapsInOneUnlockAreEachCharged() (gas: 1228603)\n[PASS] test_wrongPoolKeyRejectedEvenFromManager() (gas: 148550)\nSuite result: ok. 17 passed; 0 failed; 0 skipped; finished in 165.29ms (418.92ms CPU time)\n\nRan 7 tests for test/IMDBHookRevision.t.sol:PairFirstHookRevisionTest\n[PASS] testFuzz_midSyncPayerPaysExactlyTheSwapDeltas(bool,bool,bool,uint256,uint256) (runs: 256, μ: 1059034, ~: 1071195)\nLogs:\n  Bound result 3871\n  Bound result 6000\n\n[PASS] test_buyerWithTokenSyncedDefersBurnWithoutGain() (gas: 1158836)\n[PASS] test_deferredBurnStillRunsWithNoLiquidity() (gas: 1047455)\n[PASS] test_dustInRangeLiquidityBuybackStaysWithinBound() (gas: 1682796)\n[PASS] test_settleBeforeSwapClearsSyncAndBurnsInFlow() (gas: 821587)\n[PASS] test_twoSwapsInsideOneTokenSyncWindow() (gas: 1363656)\n[PASS] test_userSwapEndingInEmptyRangeKeepsFeePending() (gas: 840992)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 174.33ms (165.87ms CPU time)\n\nRan 28 tests for test/IMDBHook.t.sol:TokenFirstHookTest\n[PASS] testFuzz_dustRounding(uint16,bool,bool) (runs: 256, μ: 706661, ~: 729135)\nLogs:\n  Bound result 1\n\n[PASS] testFuzz_feesOnSettledDeltas(bool,bool,uint96,bool) (runs: 256, μ: 825275, ~: 837707)\nLogs:\n  Bound result 99000000000000000009205\n\n[PASS] testFuzz_prepaidSellerPaysOnlyItsInput(uint96,bool) (runs: 256, μ: 1027019, ~: 1001742)\nLogs:\n  Bound result 99999990000000000007397\n\n[PASS] test_boundedBuybackRetainsAndRetriesClaims() (gas: 1113689)\n[PASS] test_buyExactInput() (gas: 833638)\n[PASS] test_buyExactOutput() (gas: 777941)\n[PASS] test_everyEnabledCallbackRejectsForgedCallers() (gas: 167932)\n[PASS] test_factoryCanCollectAndRemoveLiquidity() (gas: 1005792)\n[PASS] test_failedSettlementRollsBackFeeAndBurn() (gas: 2171383)\n[PASS] test_freshTokenOnlyManager() (gas: 774041)\n[PASS] test_invalidConstructorParametersAndWrongAddressFlags() (gas: 6083)\n[PASS] test_invalidLimitAndExcessiveAmountRevert() (gas: 577797)\n[PASS] test_noAdministrativeEscapeHatches() (gas: 75230)\n[PASS] test_noLiquidityChargesNoFee() (gas: 955795)\n[PASS] test_partialFillBuyExactInput() (gas: 833909)\n[PASS] test_partialFillBuyExactOutput() (gas: 777629)\n[PASS] test_partialFillSellExactInput() (gas: 800392)\n[PASS] test_partialFillSellExactOutput() (gas: 860120)\n[PASS] test_permissionsAndInitialization() (gas: 128583)\n[PASS] test_prepaidBuyerStillBurnsInFlow() (gas: 823704)\n[PASS] test_prepaidSellerPaysOnlyItsInputAndBurnDefers() (gas: 1066318)\n[PASS] test_reentrantProcessingAndSwapCallbacksAreBlocked() (gas: 125998)\n[PASS] test_retryWithoutInRangeLiquidityLeavesPriceAlone() (gas: 1764803)\n[PASS] test_runtimeContainsNoUpgradeOrDestructionOpcodes() (gas: 4050730)\n[PASS] test_sellExactInput() (gas: 800253)\n[PASS] test_sellExactOutput() (gas: 860015)\n[PASS] test_sellerSettledBeforeSwapBurnsInFlow() (gas: 780037)\n[PASS] test_tokenBurnDefersUntilSellerSettles() (gas: 972183)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 183.98ms (366.98ms CPU time)\n\nRan 28 tests for test/IMDBHook.t.sol:PairFirstHookTest\n[PASS] testFuzz_dustRounding(uint16,bool,bool) (runs: 256, μ: 709310, ~: 733456)\nLogs:\n  Bound result 1\n\n[PASS] testFuzz_feesOnSettledDeltas(bool,bool,uint96,bool) (runs: 256, μ: 825936, ~: 848471)\nLogs:\n  Bound result 99000000128410190143855\n\n[PASS] testFuzz_prepaidSellerPaysOnlyItsInput(uint96,bool) (runs: 256, μ: 1019263, ~: 995811)\nLogs:\n  Bound result 1488896120860534068072\n\n[PASS] test_boundedBuybackRetainsAndRetriesClaims() (gas: 1126174)\n[PASS] test_buyExactInput() (gas: 844426)\n[PASS] test_buyExactOutput() (gas: 782487)\n[PASS] test_everyEnabledCallbackRejectsForgedCallers() (gas: 167366)\n[PASS] test_factoryCanCollectAndRemoveLiquidity() (gas: 1016559)\n[PASS] test_failedSettlementRollsBackFeeAndBurn() (gas: 2157056)\n[PASS] test_freshTokenOnlyManager() (gas: 793585)\n[PASS] test_invalidConstructorParametersAndWrongAddressFlags() (gas: 6083)\n[PASS] test_invalidLimitAndExcessiveAmountRevert() (gas: 577546)\n[PASS] test_noAdministrativeEscapeHatches() (gas: 75230)\n[PASS] test_noLiquidityChargesNoFee() (gas: 968875)\n[PASS] test_partialFillBuyExactInput() (gas: 844063)\n[PASS] test_partialFillBuyExactOutput() (gas: 782869)\n[PASS] test_partialFillSellExactInput() (gas: 796084)\n[PASS] test_partialFillSellExactOutput() (gas: 851197)\n[PASS] test_permissionsAndInitialization() (gas: 128527)\n[PASS] test_prepaidBuyerStillBurnsInFlow() (gas: 834491)\n[PASS] test_prepaidSellerPaysOnlyItsInputAndBurnDefers() (gas: 1060612)\n[PASS] test_reentrantProcessingAndSwapCallbacksAreBlocked() (gas: 125870)\n[PASS] test_retryWithoutInRangeLiquidityLeavesPriceAlone() (gas: 1777216)\n[PASS] test_runtimeContainsNoUpgradeOrDestructionOpcodes() (gas: 4050730)\n[PASS] test_sellExactInput() (gas: 795702)\n[PASS] test_sellExactOutput() (gas: 852335)\n[PASS] test_sellerSettledBeforeSwapBurnsInFlow() (gas: 775486)\n[PASS] test_tokenBurnDefersUntilSellerSettles() (gas: 960929)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 186.15ms (403.17ms CPU time)\n\nRan 1 test for test/HookInvariant.t.sol:PairFirstInvariantTest\n[PASS]\nPairFirstInvariantTest invariants:\n[PASS] invariant_feesConservedAndBurnsMatchSupply\n[PASS] invariant_noTaxOrAdminBalancesAccumulate\n PairFirstInvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭--------------+----------+-------+---------+----------╮\n| Contract     | Selector | Calls | Reverts | Discards |\n+======================================================+\n| TradeHandler | retry    | 1015  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| TradeHandler | trade    | 1033  | 0       | 0        |\n╰--------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 12500\n  Bound result 195584593970980344\n  Bound result 328459093430327468984\n  Bound result 700279009390059923553\n  Bound result 2175\n  Bound result 3069270964\n  Bound result 4194303\n  Bound result 10916297646815844\n  Bound result 7438\n  Bound result 881\n  Bound result 4143\n  Bound result 369559835174105\n  Bound result 25\n  Bound result 4\n  Bound result 5470\n  Bound result 2832\n  Bound result 3512\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 803.77ms (784.94ms CPU time)\n\nRan 1 test for test/HookInvariant.t.sol:TokenFirstInvariantTest\n[PASS]\nTokenFirstInvariantTest invariants:\n[PASS] invariant_feesConservedAndBurnsMatchSupply\n[PASS] invariant_noTaxOrAdminBalancesAccumulate\n TokenFirstInvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭--------------+----------+-------+---------+----------╮\n| Contract     | Selector | Calls | Reverts | Discards |\n+======================================================+\n| TradeHandler | retry    | 1015  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| TradeHandler | trade    | 1033  | 0       | 0        |\n╰--------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 8796\n  Bound result 195584593970980344\n  Bound result 13984\n  Bound result 304708193860844607179\n  Bound result 1063\n  Bound result 3069270964\n  Bound result 98659582791171610360\n  Bound result 10916297646815844\n  Bound result 1609\n  Bound result 2313\n  Bound result 889\n  Bound result 369559835174105\n  Bound result 12906\n  Bound result 4\n  Bound result 2514000705\n  Bound result 1814806141\n  Bound result 13866\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 849.81ms (834.10ms CPU time)\n\nRan 1 test for test/HookStatefulInvariant.t.sol:PairFirstMultiActorInvariantTest\n[PASS]\nPairFirstMultiActorInvariantTest invariants:\n[PASS] invariant_hookHoldsNoErc20AndCountersOnlyGrow\n[PASS] invariant_imdClaimsConserved\n[PASS] invariant_managerBacksHookClaimsAtRest\n[PASS] invariant_supplyFallsOnlyByHookBurns\n PairFirstMultiActorInvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭-------------------+-----------------+-------+---------+----------╮\n| Contract          | Selector        | Calls | Reverts | Discards |\n+==================================================================+\n| MultiActorHandler | addLiquidity    | 286   | 0       | 0        |\n|-------------------+-----------------+-------+---------+----------|\n| MultiActorHandler | batchTrade      | 322   | 0       | 0        |\n|-------------------+-----------------+-------+---------+----------|\n| MultiActorHandler | donateClaim     | 272   | 0       | 0        |\n|-------------------+-----------------+-------+---------+----------|\n| MultiActorHandler | midSyncTrade    | 309   | 0       | 0        |\n|-------------------+-----------------+-------+---------+----------|\n| MultiActorHandler | process         | 285   | 0       | 0        |\n|-------------------+-----------------+-------+---------+----------|\n| MultiActorHandler | removeLiquidity | 274   | 0       | 0        |\n|-------------------+-----------------+-------+---------+----------|\n| MultiActorHandler | trade           | 300   | 0       | 0        |\n╰-------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 45054907600487971676103\n  Bound result 2447\n  Bound result 53\n  Bound result 35\n  Bound result 999999999999999000\n  Bound result 49\n  Bound result 60\n  Bound result 1\n  Bound result 79\n  Bound result 25\n  Bound result 9999999000000000006019\n  Bound result 18\n  Bound result 25\n  Bound result 9999999000000404098527\n  Bound result 65535\n  Bound result 16\n  Bound result 255\n  Bound result 61166851444257674191429\n  Bound result 5676\n  Bound result 39692058013454131539810\n  Bound result 60\n  Bound result 1307\n  Bound result 70\n  Bound result 5524\n  Bound result 87994341158995389561748\n  Bound result 5998\n  Bound result 2901\n  Bound result 1170413885\n  Bound result 100\n  Bound result 8122\n  Bound result 33853236537865576610823\n  Bound result 1\n  Bound result 2475989205226037408636\n  Bound result 966298195903971068191\n  Bound result 95\n  Bound result 2476\n  Bound result 7778\n  Bound result 505\n  Bound result 29\n  Bound result 1000000000000000000000\n  Bound result 3000\n  Bound result 5\n  Bound result 43\n  Bound result 11\n  Bound result 9999999000000000000010\n  Bound result 4897\n  Bound result 2000\n  Bound result 4097000\n  Bound result 9\n  Bound result 6000\n  Bound result 1\n  Bound result 31\n  Bound result 2\n  Bound result 9999999000000000014420\n  Bound result 48879\n  Bound result 4383\n  Bound result 4096\n  Bound result 423\n  Bound result 16\n  Bound result 70\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.02s (998.80ms CPU time)\n\nRan 1 test for test/HookStatefulInvariant.t.sol:TokenFirstMultiActorInvariantTest\n[PASS]\nTokenFirstMultiActorInvariantTest invariants:\n[PASS] invariant_hookHoldsNoErc20AndCountersOnlyGrow\n[PASS] invariant_imdClaimsConserved\n[PASS] invariant_managerBacksHookClaimsAtRest\n[PASS] invariant_supplyFallsOnlyByHookBurns\n TokenFirstMultiActorInvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭-------------------+-----------------+-------+---------+----------╮\n| Contract          | Selector        | Calls | Reverts | Discards |\n+==================================================================+\n| MultiActorHandler | addLiquidity    | 286   | 0       | 0        |\n|-------------------+-----------------+-------+---------+----------|\n| MultiActorHandler | batchTrade      | 322   | 0       | 0        |\n|-------------------+-----------------+-------+---------+----------|\n| MultiActorHandler | donateClaim     | 272   | 0       | 0        |\n|-------------------+-----------------+-------+---------+----------|\n| MultiActorHandler | midSyncTrade    | 309   | 0       | 0        |\n|-------------------+-----------------+-------+---------+----------|\n| MultiActorHandler | process         | 285   | 0       | 0        |\n|-------------------+-----------------+-------+---------+----------|\n| MultiActorHandler | removeLiquidity | 274   | 0       | 0        |\n|-------------------+-----------------+-------+---------+----------|\n| MultiActorHandler | trade           | 300   | 0       | 0        |\n╰-------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 45054907600487971676103\n  Bound result 2447\n  Bound result 92\n  Bound result 35\n  Bound result 999999999999999000\n  Bound result 49\n  Bound result 60\n  Bound result 1\n  Bound result 180\n  Bound result 46\n  Bound result 9999999000000000003963\n  Bound result 18\n  Bound result 40\n  Bound result 9999999000000000000012\n  Bound result 65535\n  Bound result 16\n  Bound result 255\n  Bound result 61166851444257674191429\n  Bound result 5676\n  Bound result 39692058013454131539810\n  Bound result 60\n  Bound result 2212\n  Bound result 70\n  Bound result 1685\n  Bound result 87994341158995389561748\n  Bound result 5998\n  Bound result 235\n  Bound result 835\n  Bound result 100\n  Bound result 7867\n  Bound result 33853236537865576610823\n  Bound result 1\n  Bound result 2475989205226037408636\n  Bound result 966298195903971068191\n  Bound result 95\n  Bound result 36028797018963969\n  Bound result 826074470\n  Bound result 1216\n  Bound result 1913\n  Bound result 1000000000000000000000\n  Bound result 3000\n  Bound result 5\n  Bound result 43\n  Bound result 66\n  Bound result 9999999000000000000010\n  Bound result 2440\n  Bound result 2000\n  Bound result 4097000\n  Bound result 9\n  Bound result 6000\n  Bound result 1\n  Bound result 28\n  Bound result 2\n  Bound result 9999999000000000000646\n  Bound result 48879\n  Bound result 4383\n  Bound result 4096\n  Bound result 5294\n  Bound result 16\n  Bound result 70\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.07s (1.07s CPU time)\n\nRan 11 test suites in 1.07s (4.76s CPU time): 112 tests passed, 0 failed, 0 skipped (112 total tests)\n","passed":true},{"durationMs":92,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IMDBHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"IMDBHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"IMDBHook.processFees()\",\"IMDBHook.quoteSwap((address,address,uint24,int24,address),(bool,int256,uint160))\",\"IMDBHook.unlockCallback(bytes)\",\"IMDBToken.approve(address,uint256)\",\"IMDBToken.burn(uint256)\",\"IMDBToken.transfer(address,uint256)\",\"IMDBToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":5,\"LICENSE\":21,\"README.md\":91,\"checks/MainnetPairFork.sol.example\":53,\"docs/HOOK.md\":70,\"docs/OPERATIONS.md\":81,\"docs/SECURITY.md\":88,\"docs/dependencies.json\":152,\"foundry.toml\":22,\"package.json\":11,\"release/checksums.txt\":1,\"release/cid.txt\":1,\"release/imdbundler.car\":1040,\"remappings.txt\":4,\"scripts/configure-site.mjs\":10,\"scripts/draw-frogs.py\":43,\"scripts/mine-hook.mjs\":23,\"scripts/package-site.mjs\":72,\"scripts/serve.mjs\":19,\"site/LICENSE.txt\":21,\"site/_headers\":6,\"site/app.js\":369,\"site/assets/LICENSE.txt\":6,\"site/assets/pepe-dancing.svg\":7,\"site/assets/pepe-focused.svg\":7,\"site/assets/pepe-happy.svg\":7,\"site/assets/pepe-sad.svg\":7,\"site/assets/pepe-sleepy.svg\":7,\"site/assets/pepe-smug.svg\":7,\"site/assets/pepe-sunglasses.svg\":7,\"site/assets/pepe-thinking.svg\":7,\"site/chains.js\":12,\"site/deployment.json\":4,\"site/index.html\":122,\"site/rpc.js\":54,\"site/style.css\":8,\"site/transactions.js\":75,\"site/vault.js\":98,\"site/vendor/ethers.LICENSE.md\":21,\"site/vendor/ethers.NOTICES.txt\":145,\"site/vendor/ethers.js\":1,\"src/HookBase.sol\":85,\"src/HookFlags.sol\":29,\"src/IMDBHook.sol\":267,\"src/IMDBToken.sol\":18,\"test/HookInvariant.t.sol\":87,\"test/HookStatefulInvariant.t.sol\":302,\"test/IMDBHook.t.sol\":444,\"test/IMDBHookEdges.t.sol\":365,\"test/IMDBHookRevision.t.sol\":250,\"test/IMDBToken.t.sol\":70,\"test/mocks/BatchRouter.sol\":86,\"test/mocks/MockERC20.sol\":13,\"test/mocks/ReentryProbeManager.sol\":33,\"test/mocks/TestRouter.sol\":95,\"test/site/core.test.mjs\":164,\"test/site/dom.test.mjs\":100,\"test/site/release.test.mjs\":96,\"test/vendor/linkedom.LICENSE\":15,\"test/vendor/linkedom.NOTICES.txt\":248,\"test/vendor/linkedom.js\":12761},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"8c562e1eb519f87ee1e088b44dff95f1ac37ad95de249f93c905b5c3610fb4d2","verifiedTreeHash":"59905e92a5e7cf7ffce635a81951e3ec6024566a","verifierVersion":"0.1.0+6a374fcd"},{"checks":[{"durationMs":4677,"exitCode":0,"name":"build","output":"Compiling 82 files with Solc 0.8.26\nSolc 0.8.26 finished in 4.52s\nCompiler run successful!\n","passed":true},{"durationMs":1117,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 4 tests for test/IMDBToken.t.sol:IMDBTokenTest\n[PASS] testFuzz_transferAndBurn(uint256) (runs: 256, μ: 134038, ~: 134011)\nLogs:\n  Bound result 353611934014534643903332441\n\n[PASS] test_allowancesCannotStealOrBurnOthers() (gas: 223707)\n[PASS] test_invalidTransferAndAdminSelectorsFail() (gas: 232003)\n[PASS] test_supplyAndMetadata() (gas: 52806)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 7.87ms (8.00ms CPU time)\n\nRan 28 tests for test/IMDBHook.t.sol:TokenFirstHookTest\n[PASS] testFuzz_dustRounding(uint16,bool,bool) (runs: 256, μ: 706805, ~: 729135)\nLogs:\n  Bound result 1\n\n[PASS] testFuzz_feesOnSettledDeltas(bool,bool,uint96,bool) (runs: 256, μ: 825275, ~: 837707)\nLogs:\n  Bound result 1000000000000000000003\n\n[PASS] testFuzz_prepaidSellerPaysOnlyItsInput(uint96,bool) (runs: 256, μ: 1027019, ~: 1001742)\nLogs:\n  Bound result 99999990000000000010149\n\n[PASS] test_boundedBuybackRetainsAndRetriesClaims() (gas: 1113689)\n[PASS] test_buyExactInput() (gas: 833638)\n[PASS] test_buyExactOutput() (gas: 777941)\n[PASS] test_everyEnabledCallbackRejectsForgedCallers() (gas: 167932)\n[PASS] test_factoryCanCollectAndRemoveLiquidity() (gas: 1005792)\n[PASS] test_failedSettlementRollsBackFeeAndBurn() (gas: 2171383)\n[PASS] test_freshTokenOnlyManager() (gas: 774041)\n[PASS] test_invalidConstructorParametersAndWrongAddressFlags() (gas: 6083)\n[PASS] test_invalidLimitAndExcessiveAmountRevert() (gas: 577797)\n[PASS] test_noAdministrativeEscapeHatches() (gas: 75230)\n[PASS] test_noLiquidityChargesNoFee() (gas: 955795)\n[PASS] test_partialFillBuyExactInput() (gas: 833909)\n[PASS] test_partialFillBuyExactOutput() (gas: 777629)\n[PASS] test_partialFillSellExactInput() (gas: 800392)\n[PASS] test_partialFillSellExactOutput() (gas: 860120)\n[PASS] test_permissionsAndInitialization() (gas: 128583)\n[PASS] test_prepaidBuyerStillBurnsInFlow() (gas: 823704)\n[PASS] test_prepaidSellerPaysOnlyItsInputAndBurnDefers() (gas: 1066318)\n[PASS] test_reentrantProcessingAndSwapCallbacksAreBlocked() (gas: 125998)\n[PASS] test_retryWithoutInRangeLiquidityLeavesPriceAlone() (gas: 1764803)\n[PASS] test_runtimeContainsNoUpgradeOrDestructionOpcodes() (gas: 4050730)\n[PASS] test_sellExactInput() (gas: 800253)\n[PASS] test_sellExactOutput() (gas: 860015)\n[PASS] test_sellerSettledBeforeSwapBurnsInFlow() (gas: 780037)\n[PASS] test_tokenBurnDefersUntilSellerSettles() (gas: 972183)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 135.77ms (375.38ms CPU time)\n\nRan 28 tests for test/IMDBHook.t.sol:PairFirstHookTest\n[PASS] testFuzz_dustRounding(uint16,bool,bool) (runs: 256, μ: 709456, ~: 733456)\nLogs:\n  Bound result 2\n\n[PASS] testFuzz_feesOnSettledDeltas(bool,bool,uint96,bool) (runs: 256, μ: 825936, ~: 848471)\nLogs:\n  Bound result 326927015614029325\n\n[PASS] testFuzz_prepaidSellerPaysOnlyItsInput(uint96,bool) (runs: 256, μ: 1019263, ~: 995811)\nLogs:\n  Bound result 99999990000000000013977\n\n[PASS] test_boundedBuybackRetainsAndRetriesClaims() (gas: 1126174)\n[PASS] test_buyExactInput() (gas: 844426)\n[PASS] test_buyExactOutput() (gas: 782487)\n[PASS] test_everyEnabledCallbackRejectsForgedCallers() (gas: 167366)\n[PASS] test_factoryCanCollectAndRemoveLiquidity() (gas: 1016559)\n[PASS] test_failedSettlementRollsBackFeeAndBurn() (gas: 2157056)\n[PASS] test_freshTokenOnlyManager() (gas: 793585)\n[PASS] test_invalidConstructorParametersAndWrongAddressFlags() (gas: 6083)\n[PASS] test_invalidLimitAndExcessiveAmountRevert() (gas: 577546)\n[PASS] test_noAdministrativeEscapeHatches() (gas: 75230)\n[PASS] test_noLiquidityChargesNoFee() (gas: 968875)\n[PASS] test_partialFillBuyExactInput() (gas: 844063)\n[PASS] test_partialFillBuyExactOutput() (gas: 782869)\n[PASS] test_partialFillSellExactInput() (gas: 796084)\n[PASS] test_partialFillSellExactOutput() (gas: 851197)\n[PASS] test_permissionsAndInitialization() (gas: 128527)\n[PASS] test_prepaidBuyerStillBurnsInFlow() (gas: 834491)\n[PASS] test_prepaidSellerPaysOnlyItsInputAndBurnDefers() (gas: 1060612)\n[PASS] test_reentrantProcessingAndSwapCallbacksAreBlocked() (gas: 125870)\n[PASS] test_retryWithoutInRangeLiquidityLeavesPriceAlone() (gas: 1777216)\n[PASS] test_runtimeContainsNoUpgradeOrDestructionOpcodes() (gas: 4050730)\n[PASS] test_sellExactInput() (gas: 795702)\n[PASS] test_sellExactOutput() (gas: 852335)\n[PASS] test_sellerSettledBeforeSwapBurnsInFlow() (gas: 775486)\n[PASS] test_tokenBurnDefersUntilSellerSettles() (gas: 960929)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 145.30ms (409.92ms CPU time)\n\nRan 1 test for test/HookInvariant.t.sol:TokenFirstInvariantTest\n[PASS]\nTokenFirstInvariantTest invariants:\n[PASS] invariant_feesConservedAndBurnsMatchSupply\n[PASS] invariant_noTaxOrAdminBalancesAccumulate\n TokenFirstInvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭--------------+----------+-------+---------+----------╮\n| Contract     | Selector | Calls | Reverts | Discards |\n+======================================================+\n| TradeHandler | retry    | 1015  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| TradeHandler | trade    | 1033  | 0       | 0        |\n╰--------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 8796\n  Bound result 195584593970980344\n  Bound result 13984\n  Bound result 304708193860844607179\n  Bound result 1063\n  Bound result 3069270964\n  Bound result 98659582791171610360\n  Bound result 10916297646815844\n  Bound result 1609\n  Bound result 2313\n  Bound result 889\n  Bound result 369559835174105\n  Bound result 12906\n  Bound result 4\n  Bound result 2514000705\n  Bound result 1814806141\n  Bound result 13866\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 903.99ms (899.65ms CPU time)\n\nRan 1 test for test/HookInvariant.t.sol:PairFirstInvariantTest\n[PASS]\nPairFirstInvariantTest invariants:\n[PASS] invariant_feesConservedAndBurnsMatchSupply\n[PASS] invariant_noTaxOrAdminBalancesAccumulate\n PairFirstInvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭--------------+----------+-------+---------+----------╮\n| Contract     | Selector | Calls | Reverts | Discards |\n+======================================================+\n| TradeHandler | retry    | 1015  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| TradeHandler | trade    | 1033  | 0       | 0        |\n╰--------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 12500\n  Bound result 195584593970980344\n  Bound result 328459093430327468984\n  Bound result 700279009390059923553\n  Bound result 2175\n  Bound result 3069270964\n  Bound result 4194303\n  Bound result 10916297646815844\n  Bound result 7438\n  Bound result 881\n  Bound result 4143\n  Bound result 369559835174105\n  Bound result 25\n  Bound result 4\n  Bound result 5470\n  Bound result 2832\n  Bound result 3512\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 954.06ms (930.59ms CPU time)\n\nRan 5 test suites in 956.97ms (2.15s CPU time): 62 tests passed, 0 failed, 0 skipped (62 total tests)\n","passed":true},{"durationMs":138,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IMDBHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"IMDBHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"IMDBHook.processFees()\",\"IMDBHook.quoteSwap((address,address,uint24,int24,address),(bool,int256,uint160))\",\"IMDBHook.unlockCallback(bytes)\",\"IMDBToken.approve(address,uint256)\",\"IMDBToken.burn(uint256)\",\"IMDBToken.transfer(address,uint256)\",\"IMDBToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":5,\"LICENSE\":21,\"README.md\":91,\"checks/MainnetPairFork.sol.example\":53,\"docs/HOOK.md\":70,\"docs/OPERATIONS.md\":81,\"docs/SECURITY.md\":88,\"docs/dependencies.json\":152,\"foundry.toml\":22,\"launch.json\":30,\"package.json\":11,\"release/checksums.txt\":1,\"release/cid.txt\":1,\"release/imdbundler.car\":1040,\"remappings.txt\":4,\"scripts/configure-site.mjs\":10,\"scripts/draw-frogs.py\":43,\"scripts/mine-hook.mjs\":23,\"scripts/package-site.mjs\":72,\"scripts/serve.mjs\":19,\"site/LICENSE.txt\":21,\"site/_headers\":6,\"site/app.js\":369,\"site/assets/LICENSE.txt\":6,\"site/assets/pepe-dancing.svg\":7,\"site/assets/pepe-focused.svg\":7,\"site/assets/pepe-happy.svg\":7,\"site/assets/pepe-sad.svg\":7,\"site/assets/pepe-sleepy.svg\":7,\"site/assets/pepe-smug.svg\":7,\"site/assets/pepe-sunglasses.svg\":7,\"site/assets/pepe-thinking.svg\":7,\"site/chains.js\":12,\"site/deployment.json\":4,\"site/index.html\":122,\"site/rpc.js\":54,\"site/style.css\":8,\"site/transactions.js\":75,\"site/vault.js\":98,\"site/vendor/ethers.LICENSE.md\":21,\"site/vendor/ethers.NOTICES.txt\":145,\"site/vendor/ethers.js\":1,\"src/HookBase.sol\":85,\"src/HookFlags.sol\":29,\"src/IMDBHook.sol\":267,\"src/IMDBToken.sol\":18,\"test/HookInvariant.t.sol\":87,\"test/IMDBHook.t.sol\":444,\"test/IMDBToken.t.sol\":70,\"test/mocks/MockERC20.sol\":13,\"test/mocks/ReentryProbeManager.sol\":33,\"test/mocks/TestRouter.sol\":95,\"test/site/core.test.mjs\":164,\"test/site/dom.test.mjs\":100,\"test/site/release.test.mjs\":96,\"test/vendor/linkedom.LICENSE\":15,\"test/vendor/linkedom.NOTICES.txt\":248,\"test/vendor/linkedom.js\":12761},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"af68e0923b60647308e2942753a5fd31a62e7a2a1f1750684fd48aebdb3a58be","verifiedTreeHash":"7dca38cc5982a5fb75793b9f644ed5485ec61084","verifierVersion":"0.1.0+6a374fcd"},{"checks":[{"durationMs":2627,"exitCode":0,"name":"build","output":"Compiling 82 files with Solc 0.8.26\nSolc 0.8.26 finished in 2.53s\nCompiler run successful!\n","passed":true},{"durationMs":727,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 4 tests for test/IMDBToken.t.sol:IMDBTokenTest\n[PASS] testFuzz_transferAndBurn(uint256) (runs: 256, μ: 134037, ~: 134011)\nLogs:\n  Bound result 353611934014534643903332441\n\n[PASS] test_allowancesCannotStealOrBurnOthers() (gas: 223707)\n[PASS] test_invalidTransferAndAdminSelectorsFail() (gas: 232003)\n[PASS] test_supplyAndMetadata() (gas: 52806)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 3.97ms (4.05ms CPU time)\n\nRan 23 tests for test/IMDBHook.t.sol:TokenFirstHookTest\n[PASS] testFuzz_dustRounding(uint16,bool,bool) (runs: 256, μ: 708115, ~: 727277)\nLogs:\n  Bound result 14\n\n[PASS] testFuzz_feesOnSettledDeltas(bool,bool,uint96,bool) (runs: 256, μ: 823354, ~: 835736)\nLogs:\n  Bound result 1000000000000000000003\n\n[PASS] test_boundedBuybackRetainsAndRetriesClaims() (gas: 1110102)\n[PASS] test_buyExactInput() (gas: 831758)\n[PASS] test_buyExactOutput() (gas: 776061)\n[PASS] test_everyEnabledCallbackRejectsForgedCallers() (gas: 167837)\n[PASS] test_factoryCanCollectAndRemoveLiquidity() (gas: 1003843)\n[PASS] test_failedSettlementRollsBackFeeAndBurn() (gas: 1824342)\n[PASS] test_freshTokenOnlyManager() (gas: 771684)\n[PASS] test_invalidConstructorParametersAndWrongAddressFlags() (gas: 6083)\n[PASS] test_invalidLimitAndExcessiveAmountRevert() (gas: 577379)\n[PASS] test_noAdministrativeEscapeHatches() (gas: 75208)\n[PASS] test_noLiquidityChargesNoFee() (gas: 955558)\n[PASS] test_partialFillBuyExactInput() (gas: 832029)\n[PASS] test_partialFillBuyExactOutput() (gas: 775727)\n[PASS] test_partialFillSellExactInput() (gas: 798512)\n[PASS] test_partialFillSellExactOutput() (gas: 858152)\n[PASS] test_permissionsAndInitialization() (gas: 128494)\n[PASS] test_reentrantProcessingAndSwapCallbacksAreBlocked() (gas: 125867)\n[PASS] test_runtimeContainsNoUpgradeOrDestructionOpcodes() (gas: 3895158)\n[PASS] test_sellExactInput() (gas: 798395)\n[PASS] test_sellExactOutput() (gas: 858157)\n[PASS] test_tokenBurnDefersUntilSellerSettles() (gas: 969129)\nSuite result: ok. 23 passed; 0 failed; 0 skipped; finished in 74.55ms (126.15ms CPU time)\n\nRan 23 tests for test/IMDBHook.t.sol:PairFirstHookTest\n[PASS] testFuzz_dustRounding(uint16,bool,bool) (runs: 256, μ: 708839, ~: 731619)\nLogs:\n  Bound result 2\n\n[PASS] testFuzz_feesOnSettledDeltas(bool,bool,uint96,bool) (runs: 256, μ: 824043, ~: 846576)\nLogs:\n  Bound result 99000000128410190143855\n\n[PASS] test_boundedBuybackRetainsAndRetriesClaims() (gas: 1122156)\n[PASS] test_buyExactInput() (gas: 842567)\n[PASS] test_buyExactOutput() (gas: 780629)\n[PASS] test_everyEnabledCallbackRejectsForgedCallers() (gas: 167307)\n[PASS] test_factoryCanCollectAndRemoveLiquidity() (gas: 1014652)\n[PASS] test_failedSettlementRollsBackFeeAndBurn() (gas: 1810038)\n[PASS] test_freshTokenOnlyManager() (gas: 791252)\n[PASS] test_invalidConstructorParametersAndWrongAddressFlags() (gas: 6083)\n[PASS] test_invalidLimitAndExcessiveAmountRevert() (gas: 577164)\n[PASS] test_noAdministrativeEscapeHatches() (gas: 75208)\n[PASS] test_noLiquidityChargesNoFee() (gas: 968650)\n[PASS] test_partialFillBuyExactInput() (gas: 842204)\n[PASS] test_partialFillBuyExactOutput() (gas: 780988)\n[PASS] test_partialFillSellExactInput() (gas: 794225)\n[PASS] test_partialFillSellExactOutput() (gas: 849250)\n[PASS] test_permissionsAndInitialization() (gas: 128474)\n[PASS] test_reentrantProcessingAndSwapCallbacksAreBlocked() (gas: 125739)\n[PASS] test_runtimeContainsNoUpgradeOrDestructionOpcodes() (gas: 3895158)\n[PASS] test_sellExactInput() (gas: 793865)\n[PASS] test_sellExactOutput() (gas: 850498)\n[PASS] test_tokenBurnDefersUntilSellerSettles() (gas: 957899)\nSuite result: ok. 23 passed; 0 failed; 0 skipped; finished in 77.55ms (136.17ms CPU time)\n\nRan 1 test for test/HookInvariant.t.sol:PairFirstInvariantTest\n[PASS]\nPairFirstInvariantTest invariants:\n[PASS] invariant_feesConservedAndBurnsMatchSupply\n[PASS] invariant_noTaxOrAdminBalancesAccumulate\n PairFirstInvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭--------------+----------+-------+---------+----------╮\n| Contract     | Selector | Calls | Reverts | Discards |\n+======================================================+\n| TradeHandler | retry    | 1015  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| TradeHandler | trade    | 1033  | 0       | 0        |\n╰--------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 9694\n  Bound result 195584593970980344\n  Bound result 12358\n  Bound result 4018\n  Bound result 2908\n  Bound result 3069270964\n  Bound result 1896\n  Bound result 10916297646815844\n  Bound result 8570\n  Bound result 1200\n  Bound result 461\n  Bound result 369559835174105\n  Bound result 11589\n  Bound result 4\n  Bound result 6942\n  Bound result 579639759045378835843\n  Bound result 5588\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 613.76ms (593.89ms CPU time)\n\nRan 1 test for test/HookInvariant.t.sol:TokenFirstInvariantTest\n[PASS]\nTokenFirstInvariantTest invariants:\n[PASS] invariant_feesConservedAndBurnsMatchSupply\n[PASS] invariant_noTaxOrAdminBalancesAccumulate\n TokenFirstInvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭--------------+----------+-------+---------+----------╮\n| Contract     | Selector | Calls | Reverts | Discards |\n+======================================================+\n| TradeHandler | retry    | 1015  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| TradeHandler | trade    | 1033  | 0       | 0        |\n╰--------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 3997980755\n  Bound result 195584593970980344\n  Bound result 14239\n  Bound result 2956\n  Bound result 3434\n  Bound result 3069270964\n  Bound result 963004161583187515359\n  Bound result 10916297646815844\n  Bound result 2548616526\n  Bound result 9715\n  Bound result 2498\n  Bound result 369559835174105\n  Bound result 15296\n  Bound result 4\n  Bound result 613\n  Bound result 3105\n  Bound result 19533\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 619.46ms (598.68ms CPU time)\n\nRan 5 test suites in 621.14ms (1.39s CPU time): 52 tests passed, 0 failed, 0 skipped (52 total tests)\n","passed":true},{"durationMs":77,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IMDBHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"IMDBHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"IMDBHook.processFees()\",\"IMDBHook.quoteSwap((address,address,uint24,int24,address),(bool,int256,uint160))\",\"IMDBHook.unlockCallback(bytes)\",\"IMDBToken.approve(address,uint256)\",\"IMDBToken.burn(uint256)\",\"IMDBToken.transfer(address,uint256)\",\"IMDBToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":5,\"LICENSE\":21,\"README.md\":89,\"checks/MainnetPairFork.sol.example\":53,\"docs/HOOK.md\":68,\"docs/OPERATIONS.md\":81,\"docs/SECURITY.md\":86,\"docs/dependencies.json\":152,\"foundry.toml\":22,\"package.json\":11,\"release/checksums.txt\":1,\"release/cid.txt\":1,\"release/imdbundler.car\":1040,\"remappings.txt\":4,\"scripts/configure-site.mjs\":10,\"scripts/draw-frogs.py\":43,\"scripts/mine-hook.mjs\":23,\"scripts/package-site.mjs\":72,\"scripts/serve.mjs\":19,\"site/LICENSE.txt\":21,\"site/_headers\":6,\"site/app.js\":369,\"site/assets/LICENSE.txt\":6,\"site/assets/pepe-dancing.svg\":7,\"site/assets/pepe-focused.svg\":7,\"site/assets/pepe-happy.svg\":7,\"site/assets/pepe-sad.svg\":7,\"site/assets/pepe-sleepy.svg\":7,\"site/assets/pepe-smug.svg\":7,\"site/assets/pepe-sunglasses.svg\":7,\"site/assets/pepe-thinking.svg\":7,\"site/chains.js\":12,\"site/deployment.json\":4,\"site/index.html\":122,\"site/rpc.js\":54,\"site/style.css\":8,\"site/transactions.js\":75,\"site/vault.js\":98,\"site/vendor/ethers.LICENSE.md\":21,\"site/vendor/ethers.NOTICES.txt\":145,\"site/vendor/ethers.js\":1,\"src/HookBase.sol\":85,\"src/HookFlags.sol\":29,\"src/IMDBHook.sol\":260,\"src/IMDBToken.sol\":18,\"test/HookInvariant.t.sol\":87,\"test/IMDBHook.t.sol\":350,\"test/IMDBToken.t.sol\":70,\"test/mocks/MockERC20.sol\":13,\"test/mocks/ReentryProbeManager.sol\":33,\"test/mocks/TestRouter.sol\":62,\"test/site/core.test.mjs\":164,\"test/site/dom.test.mjs\":100,\"test/site/release.test.mjs\":96,\"test/vendor/linkedom.LICENSE\":15,\"test/vendor/linkedom.NOTICES.txt\":248,\"test/vendor/linkedom.js\":12761},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":1818,"exitCode":0,"name":"slither","output":"[medium/high] incorrect-equality at src/IMDBHook.sol:234: IMDBHook._burnAvailableTokens() (src/IMDBHook.sol#234-246) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/IMDBHook.sol:183: Reentrancy in IMDBHook.processFees() (src/IMDBHook.sol#183-188):\n[medium/medium] reentrancy-no-eth at src/IMDBHook.sol:130: Reentrancy in IMDBHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/IMDBHook.sol#130-155):\n[medium/medium] unused-return at src/IMDBHook.sol:204: IMDBHook._process(PoolKey) (src/IMDBHook.sol#204-232) ignores return value by (price,None,None,None) = poolManager.getSlot0(key.toId()) (src/IMDBHook.sol#210)\n[low/medium] reentrancy-benign at src/IMDBHook.sol:96: Reentrancy in IMDBHook.beforeSwap(address,PoolKey,SwapParams,bytes) (src/IMDBHook.sol#96-128):\n[low/medium] reentrancy-benign at src/IMDBHook.sol:204: Reentrancy in IMDBHook._process(PoolKey) (src/IMDBHook.sol#204-232):\n[low/medium] reentrancy-events at src/IMDBHook.sol:130: Reentrancy in IMDBHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/IMDBHook.sol#130-155):\n[low/medium] reentrancy-events at src/IMDBHook.sol:204: Reentrancy in IMDBHook._process(PoolKey) (src/IMDBHook.sol#204-232):\n[low/medium] reentrancy-events at src/IMDBHook.sol:234: Reentrancy in IMDBHook._burnAvailableTokens() (src/IMDBHook.sol#234-246):\n[low/medium] reentrancy-events at src/IMDBHook.sol:204: Reentrancy in IMDBHook._process(PoolKey) (src/IMDBHook.sol#204-232):\n[low/medium] reentrancy-events at src/IMDBHook.sol:130: Reentrancy in IMDBHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/IMDBHook.sol#130-155):","passed":true},{"durationMs":1408,"exitCode":0,"name":"aderyn","output":"[high] reentrancy-state-change at src/IMDBHook.sol:148: Reentrancy: State change after external call (2 places)\n[low] internal-function-used-once at src/HookFlags.sol:22: Internal Function Used Only Once\n[low] large-numeric-literal at src/IMDBHook.sol:212: Large Numeric Literal (5 places)\n[low] literal-instead-of-constant at src/IMDBHook.sol:115: Literal Instead of Constant (12 places)\n[low] state-change-without-event at src/IMDBHook.sol:96: State Change Without Event (2 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"beae4f17c2284e4e9e6f1776abaa007ad6c0cbbf8ea5befe95ac6069b828c7e7","verifiedTreeHash":"0f3f727981d4289a65a09774487a1d3051b1df74","verifierVersion":"0.1.0+6a374fcd"},{"checks":[{"durationMs":6457,"exitCode":0,"name":"build","output":"Compiling 82 files with Solc 0.8.26\nSolc 0.8.26 finished in 6.21s\nCompiler run successful!\n","passed":true},{"durationMs":1502,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 4 tests for test/IMDBToken.t.sol:IMDBTokenTest\n[PASS] testFuzz_transferAndBurn(uint256) (runs: 256, μ: 134038, ~: 134011)\nLogs:\n  Bound result 353611934014534643903332441\n\n[PASS] test_allowancesCannotStealOrBurnOthers() (gas: 223707)\n[PASS] test_invalidTransferAndAdminSelectorsFail() (gas: 232003)\n[PASS] test_supplyAndMetadata() (gas: 52806)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 10.90ms (10.83ms CPU time)\n\nRan 28 tests for test/IMDBHook.t.sol:TokenFirstHookTest\n[PASS] testFuzz_dustRounding(uint16,bool,bool) (runs: 256, μ: 706805, ~: 729135)\nLogs:\n  Bound result 2\n\n[PASS] testFuzz_feesOnSettledDeltas(bool,bool,uint96,bool) (runs: 256, μ: 825275, ~: 837707)\nLogs:\n  Bound result 99000000128410190143855\n\n[PASS] testFuzz_prepaidSellerPaysOnlyItsInput(uint96,bool) (runs: 256, μ: 1027019, ~: 1001742)\nLogs:\n  Bound result 99999990000000000007397\n\n[PASS] test_boundedBuybackRetainsAndRetriesClaims() (gas: 1113689)\n[PASS] test_buyExactInput() (gas: 833638)\n[PASS] test_buyExactOutput() (gas: 777941)\n[PASS] test_everyEnabledCallbackRejectsForgedCallers() (gas: 167932)\n[PASS] test_factoryCanCollectAndRemoveLiquidity() (gas: 1005792)\n[PASS] test_failedSettlementRollsBackFeeAndBurn() (gas: 2171383)\n[PASS] test_freshTokenOnlyManager() (gas: 774041)\n[PASS] test_invalidConstructorParametersAndWrongAddressFlags() (gas: 6083)\n[PASS] test_invalidLimitAndExcessiveAmountRevert() (gas: 577797)\n[PASS] test_noAdministrativeEscapeHatches() (gas: 75230)\n[PASS] test_noLiquidityChargesNoFee() (gas: 955795)\n[PASS] test_partialFillBuyExactInput() (gas: 833909)\n[PASS] test_partialFillBuyExactOutput() (gas: 777629)\n[PASS] test_partialFillSellExactInput() (gas: 800392)\n[PASS] test_partialFillSellExactOutput() (gas: 860120)\n[PASS] test_permissionsAndInitialization() (gas: 128583)\n[PASS] test_prepaidBuyerStillBurnsInFlow() (gas: 823704)\n[PASS] test_prepaidSellerPaysOnlyItsInputAndBurnDefers() (gas: 1066318)\n[PASS] test_reentrantProcessingAndSwapCallbacksAreBlocked() (gas: 125998)\n[PASS] test_retryWithoutInRangeLiquidityLeavesPriceAlone() (gas: 1764803)\n[PASS] test_runtimeContainsNoUpgradeOrDestructionOpcodes() (gas: 4050730)\n[PASS] test_sellExactInput() (gas: 800253)\n[PASS] test_sellExactOutput() (gas: 860015)\n[PASS] test_sellerSettledBeforeSwapBurnsInFlow() (gas: 780037)\n[PASS] test_tokenBurnDefersUntilSellerSettles() (gas: 972183)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 219.79ms (646.20ms CPU time)\n\nRan 28 tests for test/IMDBHook.t.sol:PairFirstHookTest\n[PASS] testFuzz_dustRounding(uint16,bool,bool) (runs: 256, μ: 709456, ~: 733456)\nLogs:\n  Bound result 1\n\n[PASS] testFuzz_feesOnSettledDeltas(bool,bool,uint96,bool) (runs: 256, μ: 825936, ~: 848471)\nLogs:\n  Bound result 1000000000000000000003\n\n[PASS] testFuzz_prepaidSellerPaysOnlyItsInput(uint96,bool) (runs: 256, μ: 1019263, ~: 995811)\nLogs:\n  Bound result 99999990000000000020773\n\n[PASS] test_boundedBuybackRetainsAndRetriesClaims() (gas: 1126174)\n[PASS] test_buyExactInput() (gas: 844426)\n[PASS] test_buyExactOutput() (gas: 782487)\n[PASS] test_everyEnabledCallbackRejectsForgedCallers() (gas: 167366)\n[PASS] test_factoryCanCollectAndRemoveLiquidity() (gas: 1016559)\n[PASS] test_failedSettlementRollsBackFeeAndBurn() (gas: 2157056)\n[PASS] test_freshTokenOnlyManager() (gas: 793585)\n[PASS] test_invalidConstructorParametersAndWrongAddressFlags() (gas: 6083)\n[PASS] test_invalidLimitAndExcessiveAmountRevert() (gas: 577546)\n[PASS] test_noAdministrativeEscapeHatches() (gas: 75230)\n[PASS] test_noLiquidityChargesNoFee() (gas: 968875)\n[PASS] test_partialFillBuyExactInput() (gas: 844063)\n[PASS] test_partialFillBuyExactOutput() (gas: 782869)\n[PASS] test_partialFillSellExactInput() (gas: 796084)\n[PASS] test_partialFillSellExactOutput() (gas: 851197)\n[PASS] test_permissionsAndInitialization() (gas: 128527)\n[PASS] test_prepaidBuyerStillBurnsInFlow() (gas: 834491)\n[PASS] test_prepaidSellerPaysOnlyItsInputAndBurnDefers() (gas: 1060612)\n[PASS] test_reentrantProcessingAndSwapCallbacksAreBlocked() (gas: 125870)\n[PASS] test_retryWithoutInRangeLiquidityLeavesPriceAlone() (gas: 1777216)\n[PASS] test_runtimeContainsNoUpgradeOrDestructionOpcodes() (gas: 4050730)\n[PASS] test_sellExactInput() (gas: 795702)\n[PASS] test_sellExactOutput() (gas: 852335)\n[PASS] test_sellerSettledBeforeSwapBurnsInFlow() (gas: 775486)\n[PASS] test_tokenBurnDefersUntilSellerSettles() (gas: 960929)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 230.39ms (570.20ms CPU time)\n\nRan 1 test for test/HookInvariant.t.sol:PairFirstInvariantTest\n[PASS]\nPairFirstInvariantTest invariants:\n[PASS] invariant_feesConservedAndBurnsMatchSupply\n[PASS] invariant_noTaxOrAdminBalancesAccumulate\n PairFirstInvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭--------------+----------+-------+---------+----------╮\n| Contract     | Selector | Calls | Reverts | Discards |\n+======================================================+\n| TradeHandler | retry    | 1015  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| TradeHandler | trade    | 1033  | 0       | 0        |\n╰--------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 12500\n  Bound result 195584593970980344\n  Bound result 328459093430327468984\n  Bound result 700279009390059923553\n  Bound result 2175\n  Bound result 3069270964\n  Bound result 4194303\n  Bound result 10916297646815844\n  Bound result 7438\n  Bound result 881\n  Bound result 4143\n  Bound result 369559835174105\n  Bound result 25\n  Bound result 4\n  Bound result 5470\n  Bound result 2832\n  Bound result 3512\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.28s (1.24s CPU time)\n\nRan 1 test for test/HookInvariant.t.sol:TokenFirstInvariantTest\n[PASS]\nTokenFirstInvariantTest invariants:\n[PASS] invariant_feesConservedAndBurnsMatchSupply\n[PASS] invariant_noTaxOrAdminBalancesAccumulate\n TokenFirstInvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭--------------+----------+-------+---------+----------╮\n| Contract     | Selector | Calls | Reverts | Discards |\n+======================================================+\n| TradeHandler | retry    | 1015  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| TradeHandler | trade    | 1033  | 0       | 0        |\n╰--------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 8796\n  Bound result 195584593970980344\n  Bound result 13984\n  Bound result 304708193860844607179\n  Bound result 1063\n  Bound result 3069270964\n  Bound result 98659582791171610360\n  Bound result 10916297646815844\n  Bound result 1609\n  Bound result 2313\n  Bound result 889\n  Bound result 369559835174105\n  Bound result 12906\n  Bound result 4\n  Bound result 2514000705\n  Bound result 1814806141\n  Bound result 13866\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.28s (1.27s CPU time)\n\nRan 5 test suites in 1.29s (3.02s CPU time): 62 tests passed, 0 failed, 0 skipped (62 total tests)\n","passed":true},{"durationMs":198,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IMDBHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"IMDBHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"IMDBHook.processFees()\",\"IMDBHook.quoteSwap((address,address,uint24,int24,address),(bool,int256,uint160))\",\"IMDBHook.unlockCallback(bytes)\",\"IMDBToken.approve(address,uint256)\",\"IMDBToken.burn(uint256)\",\"IMDBToken.transfer(address,uint256)\",\"IMDBToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":5,\"LICENSE\":21,\"README.md\":91,\"checks/MainnetPairFork.sol.example\":53,\"docs/HOOK.md\":70,\"docs/OPERATIONS.md\":81,\"docs/SECURITY.md\":88,\"docs/dependencies.json\":152,\"foundry.toml\":22,\"package.json\":11,\"release/checksums.txt\":1,\"release/cid.txt\":1,\"release/imdbundler.car\":1040,\"remappings.txt\":4,\"scripts/configure-site.mjs\":10,\"scripts/draw-frogs.py\":43,\"scripts/mine-hook.mjs\":23,\"scripts/package-site.mjs\":72,\"scripts/serve.mjs\":19,\"site/LICENSE.txt\":21,\"site/_headers\":6,\"site/app.js\":369,\"site/assets/LICENSE.txt\":6,\"site/assets/pepe-dancing.svg\":7,\"site/assets/pepe-focused.svg\":7,\"site/assets/pepe-happy.svg\":7,\"site/assets/pepe-sad.svg\":7,\"site/assets/pepe-sleepy.svg\":7,\"site/assets/pepe-smug.svg\":7,\"site/assets/pepe-sunglasses.svg\":7,\"site/assets/pepe-thinking.svg\":7,\"site/chains.js\":12,\"site/deployment.json\":4,\"site/index.html\":122,\"site/rpc.js\":54,\"site/style.css\":8,\"site/transactions.js\":75,\"site/vault.js\":98,\"site/vendor/ethers.LICENSE.md\":21,\"site/vendor/ethers.NOTICES.txt\":145,\"site/vendor/ethers.js\":1,\"src/HookBase.sol\":85,\"src/HookFlags.sol\":29,\"src/IMDBHook.sol\":267,\"src/IMDBToken.sol\":18,\"test/HookInvariant.t.sol\":87,\"test/IMDBHook.t.sol\":444,\"test/IMDBToken.t.sol\":70,\"test/mocks/MockERC20.sol\":13,\"test/mocks/ReentryProbeManager.sol\":33,\"test/mocks/TestRouter.sol\":95,\"test/site/core.test.mjs\":164,\"test/site/dom.test.mjs\":100,\"test/site/release.test.mjs\":96,\"test/vendor/linkedom.LICENSE\":15,\"test/vendor/linkedom.NOTICES.txt\":248,\"test/vendor/linkedom.js\":12761},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":3871,"exitCode":0,"name":"slither","output":"[medium/high] incorrect-equality at src/IMDBHook.sol:238: IMDBHook._burnAvailableTokens() (src/IMDBHook.sol#238-253) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/IMDBHook.sol:132: Reentrancy in IMDBHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/IMDBHook.sol#132-157):\n[medium/medium] reentrancy-no-eth at src/IMDBHook.sol:185: Reentrancy in IMDBHook.processFees() (src/IMDBHook.sol#185-190):\n[medium/medium] unused-return at src/IMDBHook.sol:206: IMDBHook._process(PoolKey) (src/IMDBHook.sol#206-236) ignores return value by (price,None,None,None) = poolManager.getSlot0(key.toId()) (src/IMDBHook.sol#214)\n[low/medium] reentrancy-benign at src/IMDBHook.sol:98: Reentrancy in IMDBHook.beforeSwap(address,PoolKey,SwapParams,bytes) (src/IMDBHook.sol#98-130):\n[low/medium] reentrancy-benign at src/IMDBHook.sol:206: Reentrancy in IMDBHook._process(PoolKey) (src/IMDBHook.sol#206-236):\n[low/medium] reentrancy-events at src/IMDBHook.sol:132: Reentrancy in IMDBHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/IMDBHook.sol#132-157):\n[low/medium] reentrancy-events at src/IMDBHook.sol:238: Reentrancy in IMDBHook._burnAvailableTokens() (src/IMDBHook.sol#238-253):\n[low/medium] reentrancy-events at src/IMDBHook.sol:206: Reentrancy in IMDBHook._process(PoolKey) (src/IMDBHook.sol#206-236):\n[low/medium] reentrancy-events at src/IMDBHook.sol:132: Reentrancy in IMDBHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/IMDBHook.sol#132-157):\n[low/medium] reentrancy-events at src/IMDBHook.sol:206: Reentrancy in IMDBHook._process(PoolKey) (src/IMDBHook.sol#206-236):","passed":true},{"durationMs":1124,"exitCode":0,"name":"aderyn","output":"[high] reentrancy-state-change at src/IMDBHook.sol:150: Reentrancy: State change after external call (2 places)\n[low] internal-function-used-once at src/HookFlags.sol:22: Internal Function Used Only Once\n[low] large-numeric-literal at src/IMDBHook.sol:216: Large Numeric Literal (5 places)\n[low] literal-instead-of-constant at src/IMDBHook.sol:117: Literal Instead of Constant (12 places)\n[low] state-change-without-event at src/IMDBHook.sol:98: State Change Without Event (2 places)","passed":true},{"durationMs":5037,"exitCode":0,"name":"proof 841aea9687e7","output":"Compiling 80 files with Solc 0.8.26\nSolc 0.8.26 finished in 3.52s\nCompiler run successful!\n\nRan 1 test for test/imd-proof-990afbd0/Proof_841aea9687e7.t.sol:PrepaidSellerOverpaysTest\n[PASS] test_exactInputSellerPaysOnlyTheSoldAmount() (gas: 12004219)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 16.89ms (16.22ms CPU time)\n\nRan 1 test suite in 19.19ms (16.89ms 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":"df54acfb9b8fc68a8b0ab43d6a2ad8fa65d1e2ec73aa4eb1b2976cb095f48295","verifiedTreeHash":"b55a25aaea074dc4969045503691a1c4e50153e1","verifierVersion":"0.1.0+6a374fcd"},{"checks":[{"durationMs":9582,"exitCode":0,"name":"build","output":"Compiling 85 files with Solc 0.8.26\nSolc 0.8.26 finished in 9.33s\nCompiler run successful!\n","passed":true},{"durationMs":1861,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 4 tests for test/IMDBToken.t.sol:IMDBTokenTest\n[PASS] testFuzz_transferAndBurn(uint256) (runs: 256, μ: 134019, ~: 134011)\nLogs:\n  Bound result 299\n\n[PASS] test_allowancesCannotStealOrBurnOthers() (gas: 223707)\n[PASS] test_invalidTransferAndAdminSelectorsFail() (gas: 232003)\n[PASS] test_supplyAndMetadata() (gas: 52806)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 9.51ms (9.97ms CPU time)\n\nRan 23 tests for test/IMDBHook.t.sol:PairFirstHookTest\n[PASS] testFuzz_dustRounding(uint16,bool,bool) (runs: 256, μ: 708496, ~: 731619)\nLogs:\n  Bound result 3492\n\n[PASS] testFuzz_feesOnSettledDeltas(bool,bool,uint96,bool) (runs: 256, μ: 824043, ~: 846576)\nLogs:\n  Bound result 99000000128410190143855\n\n[PASS] test_boundedBuybackRetainsAndRetriesClaims() (gas: 1122156)\n[PASS] test_buyExactInput() (gas: 842567)\n[PASS] test_buyExactOutput() (gas: 780629)\n[PASS] test_everyEnabledCallbackRejectsForgedCallers() (gas: 167307)\n[PASS] test_factoryCanCollectAndRemoveLiquidity() (gas: 1014652)\n[PASS] test_failedSettlementRollsBackFeeAndBurn() (gas: 1810038)\n[PASS] test_freshTokenOnlyManager() (gas: 791252)\n[PASS] test_invalidConstructorParametersAndWrongAddressFlags() (gas: 6083)\n[PASS] test_invalidLimitAndExcessiveAmountRevert() (gas: 577164)\n[PASS] test_noAdministrativeEscapeHatches() (gas: 75208)\n[PASS] test_noLiquidityChargesNoFee() (gas: 968650)\n[PASS] test_partialFillBuyExactInput() (gas: 842204)\n[PASS] test_partialFillBuyExactOutput() (gas: 780988)\n[PASS] test_partialFillSellExactInput() (gas: 794225)\n[PASS] test_partialFillSellExactOutput() (gas: 849250)\n[PASS] test_permissionsAndInitialization() (gas: 128474)\n[PASS] test_reentrantProcessingAndSwapCallbacksAreBlocked() (gas: 125739)\n[PASS] test_runtimeContainsNoUpgradeOrDestructionOpcodes() (gas: 3895158)\n[PASS] test_sellExactInput() (gas: 793865)\n[PASS] test_sellExactOutput() (gas: 850498)\n[PASS] test_tokenBurnDefersUntilSellerSettles() (gas: 957899)\nSuite result: ok. 23 passed; 0 failed; 0 skipped; finished in 279.72ms (387.51ms CPU time)\n\nRan 23 tests for test/IMDBHook.t.sol:TokenFirstHookTest\n[PASS] testFuzz_dustRounding(uint16,bool,bool) (runs: 256, μ: 707804, ~: 727277)\nLogs:\n  Bound result 1\n\n[PASS] testFuzz_feesOnSettledDeltas(bool,bool,uint96,bool) (runs: 256, μ: 823354, ~: 835736)\nLogs:\n  Bound result 99000000128410190143855\n\n[PASS] test_boundedBuybackRetainsAndRetriesClaims() (gas: 1110102)\n[PASS] test_buyExactInput() (gas: 831758)\n[PASS] test_buyExactOutput() (gas: 776061)\n[PASS] test_everyEnabledCallbackRejectsForgedCallers() (gas: 167837)\n[PASS] test_factoryCanCollectAndRemoveLiquidity() (gas: 1003843)\n[PASS] test_failedSettlementRollsBackFeeAndBurn() (gas: 1824342)\n[PASS] test_freshTokenOnlyManager() (gas: 771684)\n[PASS] test_invalidConstructorParametersAndWrongAddressFlags() (gas: 6083)\n[PASS] test_invalidLimitAndExcessiveAmountRevert() (gas: 577379)\n[PASS] test_noAdministrativeEscapeHatches() (gas: 75208)\n[PASS] test_noLiquidityChargesNoFee() (gas: 955558)\n[PASS] test_partialFillBuyExactInput() (gas: 832029)\n[PASS] test_partialFillBuyExactOutput() (gas: 775727)\n[PASS] test_partialFillSellExactInput() (gas: 798512)\n[PASS] test_partialFillSellExactOutput() (gas: 858152)\n[PASS] test_permissionsAndInitialization() (gas: 128494)\n[PASS] test_reentrantProcessingAndSwapCallbacksAreBlocked() (gas: 125867)\n[PASS] test_runtimeContainsNoUpgradeOrDestructionOpcodes() (gas: 3895158)\n[PASS] test_sellExactInput() (gas: 798395)\n[PASS] test_sellExactOutput() (gas: 858157)\n[PASS] test_tokenBurnDefersUntilSellerSettles() (gas: 969129)\nSuite result: ok. 23 passed; 0 failed; 0 skipped; finished in 279.74ms (228.80ms CPU time)\n\nRan 17 tests for test/IMDBHookEdges.t.sol:PairFirstHookEdgeTest\n[PASS] testFuzz_exactInputBuyNeverDebitsMoreThanBudget(uint256) (runs: 256, μ: 802486, ~: 816332)\nLogs:\n  Bound result 299\n\n[PASS] testFuzz_exactOutputSellDeliversRequestedNet(uint256) (runs: 256, μ: 809618, ~: 823819)\nLogs:\n  Bound result 212112439371432361213192\n\n[PASS] testFuzz_protocolFeeDoesNotBreakFeeAccounting(bool,bool,uint256,uint256) (runs: 256, μ: 921880, ~: 902646)\nLogs:\n  Bound result 300000000000000000000000\n  Bound result 16224\n\n[PASS] testFuzz_randomPriceLimitsNeverMischargeOrRevert(bool,bool,uint256,uint256) (runs: 256, μ: 805218, ~: 806177)\nLogs:\n  Bound result 11654506603753121813732\n  Bound result 6204\n\n[PASS] test_buybackSkippedAtTerminalPriceRetainsClaimWithoutReverting() (gas: 1495396)\n[PASS] test_callbackPhaseGuards() (gas: 250783)\n[PASS] test_disabledCallbacksRevertEvenForManager() (gas: 125848)\n[PASS] test_donateToPoolSkipsHookAndChargesNothing() (gas: 453073)\n[PASS] test_donatedImdClaimsAreBoughtBackAndBurnedNotWithdrawable() (gas: 690877)\n[PASS] test_exactInputBuyRoundingBoundaries() (gas: 1896343)\n[PASS] test_freshManagerTokenOnlyPoolExactOutputBuy() (gas: 709836)\n[PASS] test_hookPinsTheLaunchConfiguration() (gas: 66035)\n[PASS] test_processFeesInsideAnotherUnlockReverts() (gas: 922164)\n[PASS] test_processFeesWithNothingPendingIsANoOpForAnyone() (gas: 500219)\n[PASS] test_specifiedAmountBounds() (gas: 225595)\n[PASS] test_twoOppositeSwapsInOneUnlockAreEachCharged() (gas: 1221842)\n[PASS] test_wrongPoolKeyRejectedEvenFromManager() (gas: 148586)\nSuite result: ok. 17 passed; 0 failed; 0 skipped; finished in 361.96ms (1.05s CPU time)\n\nRan 17 tests for test/IMDBHookEdges.t.sol:TokenFirstHookEdgeTest\n[PASS] testFuzz_exactInputBuyNeverDebitsMoreThanBudget(uint256) (runs: 256, μ: 791175, ~: 805025)\nLogs:\n  Bound result 2174\n\n[PASS] testFuzz_exactOutputSellDeliversRequestedNet(uint256) (runs: 256, μ: 817522, ~: 831282)\nLogs:\n  Bound result 212112439371432361213192\n\n[PASS] testFuzz_protocolFeeDoesNotBreakFeeAccounting(bool,bool,uint256,uint256) (runs: 256, μ: 922009, ~: 908721)\nLogs:\n  Bound result 299999000000000922199200\n  Bound result 286006\n\n[PASS] testFuzz_randomPriceLimitsNeverMischargeOrRevert(bool,bool,uint256,uint256) (runs: 256, μ: 803755, ~: 802335)\nLogs:\n  Bound result 300000000000000000000000\n  Bound result 6840\n\n[PASS] test_buybackSkippedAtTerminalPriceRetainsClaimWithoutReverting() (gas: 1475890)\n[PASS] test_callbackPhaseGuards() (gas: 249987)\n[PASS] test_disabledCallbacksRevertEvenForManager() (gas: 125848)\n[PASS] test_donateToPoolSkipsHookAndChargesNothing() (gas: 453073)\n[PASS] test_donatedImdClaimsAreBoughtBackAndBurnedNotWithdrawable() (gas: 685482)\n[PASS] test_exactInputBuyRoundingBoundaries() (gas: 1872235)\n[PASS] test_freshManagerTokenOnlyPoolExactOutputBuy() (gas: 698530)\n[PASS] test_hookPinsTheLaunchConfiguration() (gas: 66055)\n[PASS] test_processFeesInsideAnotherUnlockReverts() (gas: 910411)\n[PASS] test_processFeesWithNothingPendingIsANoOpForAnyone() (gas: 500235)\n[PASS] test_specifiedAmountBounds() (gas: 225289)\n[PASS] test_twoOppositeSwapsInOneUnlockAreEachCharged() (gas: 1209316)\n[PASS] test_wrongPoolKeyRejectedEvenFromManager() (gas: 147868)\nSuite result: ok. 17 passed; 0 failed; 0 skipped; finished in 362.01ms (1.28s CPU time)\n\nRan 1 test for test/HookInvariant.t.sol:TokenFirstInvariantTest\n[PASS]\nTokenFirstInvariantTest invariants:\n[PASS] invariant_feesConservedAndBurnsMatchSupply\n[PASS] invariant_noTaxOrAdminBalancesAccumulate\n TokenFirstInvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭--------------+----------+-------+---------+----------╮\n| Contract     | Selector | Calls | Reverts | Discards |\n+======================================================+\n| TradeHandler | retry    | 1015  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| TradeHandler | trade    | 1033  | 0       | 0        |\n╰--------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 3997980755\n  Bound result 195584593970980344\n  Bound result 14239\n  Bound result 2956\n  Bound result 3434\n  Bound result 3069270964\n  Bound result 963004161583187515359\n  Bound result 10916297646815844\n  Bound result 2548616526\n  Bound result 9715\n  Bound result 2498\n  Bound result 369559835174105\n  Bound result 15296\n  Bound result 4\n  Bound result 613\n  Bound result 3105\n  Bound result 19533\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.22s (1.15s CPU time)\n\nRan 1 test for test/HookInvariant.t.sol:PairFirstInvariantTest\n[PASS]\nPairFirstInvariantTest invariants:\n[PASS] invariant_feesConservedAndBurnsMatchSupply\n[PASS] invariant_noTaxOrAdminBalancesAccumulate\n PairFirstInvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭--------------+----------+-------+---------+----------╮\n| Contract     | Selector | Calls | Reverts | Discards |\n+======================================================+\n| TradeHandler | retry    | 1015  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| TradeHandler | trade    | 1033  | 0       | 0        |\n╰--------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 9694\n  Bound result 195584593970980344\n  Bound result 12358\n  Bound result 4018\n  Bound result 2908\n  Bound result 3069270964\n  Bound result 1896\n  Bound result 10916297646815844\n  Bound result 8570\n  Bound result 1200\n  Bound result 461\n  Bound result 369559835174105\n  Bound result 11589\n  Bound result 4\n  Bound result 6942\n  Bound result 579639759045378835843\n  Bound result 5588\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.23s (1.16s CPU time)\n\nRan 1 test for test/HookStatefulInvariant.t.sol:PairFirstMultiActorInvariantTest\n[PASS]\nPairFirstMultiActorInvariantTest invariants:\n[PASS] invariant_hookHoldsNoErc20AndCountersOnlyGrow\n[PASS] invariant_imdClaimsConserved\n[PASS] invariant_managerBacksHookClaimsAtRest\n[PASS] invariant_supplyFallsOnlyByHookBurns\n PairFirstMultiActorInvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭-------------------+-----------------+-------+---------+----------╮\n| Contract          | Selector        | Calls | Reverts | Discards |\n+==================================================================+\n| MultiActorHandler | addLiquidity    | 352   | 0       | 0        |\n|-------------------+-----------------+-------+---------+----------|\n| MultiActorHandler | batchTrade      | 331   | 0       | 0        |\n|-------------------+-----------------+-------+---------+----------|\n| MultiActorHandler | donateClaim     | 375   | 0       | 0        |\n|-------------------+-----------------+-------+---------+----------|\n| MultiActorHandler | process         | 327   | 0       | 0        |\n|-------------------+-----------------+-------+---------+----------|\n| MultiActorHandler | removeLiquidity | 331   | 0       | 0        |\n|-------------------+-----------------+-------+---------+----------|\n| MultiActorHandler | trade           | 332   | 0       | 0        |\n╰-------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 8256\n  Bound result 1155\n  Bound result 8396\n  Bound result 96265667538422919204571\n  Bound result 5148\n  Bound result 405\n  Bound result 1\n  Bound result 14\n  Bound result 1258195623\n  Bound result 45054907600487971676103\n  Bound result 2447\n  Bound result 28\n  Bound result 100\n  Bound result 9999999000000000000243\n  Bound result 126\n  Bound result 500\n  Bound result 1\n  Bound result 15\n  Bound result 83\n  Bound result 9999999000000000007822\n  Bound result 18\n  Bound result 15\n  Bound result 9999999000000000000785\n  Bound result 10000000000000000\n  Bound result 5\n  Bound result 39062500000000\n  Bound result 61166851444257674191429\n  Bound result 5676\n  Bound result 39692058013454131539810\n  Bound result 12500\n  Bound result 4260\n  Bound result 70\n  Bound result 1626\n  Bound result 87994341158995389561748\n  Bound result 5998\n  Bound result 4714\n  Bound result 80\n  Bound result 64\n  Bound result 9999999000000000000012\n  Bound result 7\n  Bound result 92\n  Bound result 9999999000000000002049\n  Bound result 95\n  Bound result 100\n  Bound result 9999999000000000000007\n  Bound result 43\n  Bound result 40\n  Bound result 4999999999999995000\n  Bound result 9\n  Bound result 512\n  Bound result 1\n  Bound result 200\n  Bound result 100\n  Bound result 1000000000000000\n  Bound result 40451043261732225208159\n  Bound result 4214\n  Bound result 3\n  Bound result 3393\n  Bound result 1\n  Bound result 23\n  Bound result 9999999899999990\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.50s (1.45s CPU time)\n\nRan 1 test for test/HookStatefulInvariant.t.sol:TokenFirstMultiActorInvariantTest\n[PASS]\nTokenFirstMultiActorInvariantTest invariants:\n[PASS] invariant_hookHoldsNoErc20AndCountersOnlyGrow\n[PASS] invariant_imdClaimsConserved\n[PASS] invariant_managerBacksHookClaimsAtRest\n[PASS] invariant_supplyFallsOnlyByHookBurns\n TokenFirstMultiActorInvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭-------------------+-----------------+-------+---------+----------╮\n| Contract          | Selector        | Calls | Reverts | Discards |\n+==================================================================+\n| MultiActorHandler | addLiquidity    | 352   | 0       | 0        |\n|-------------------+-----------------+-------+---------+----------|\n| MultiActorHandler | batchTrade      | 331   | 0       | 0        |\n|-------------------+-----------------+-------+---------+----------|\n| MultiActorHandler | donateClaim     | 375   | 0       | 0        |\n|-------------------+-----------------+-------+---------+----------|\n| MultiActorHandler | process         | 327   | 0       | 0        |\n|-------------------+-----------------+-------+---------+----------|\n| MultiActorHandler | removeLiquidity | 331   | 0       | 0        |\n|-------------------+-----------------+-------+---------+----------|\n| MultiActorHandler | trade           | 332   | 0       | 0        |\n╰-------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 139644302\n  Bound result 2162\n  Bound result 8396\n  Bound result 96265667538422919204571\n  Bound result 5148\n  Bound result 2943432038\n  Bound result 1\n  Bound result 14\n  Bound result 9129\n  Bound result 45054907600487971676103\n  Bound result 2447\n  Bound result 89\n  Bound result 100\n  Bound result 9999999000000000000243\n  Bound result 126\n  Bound result 500\n  Bound result 1\n  Bound result 66\n  Bound result 95\n  Bound result 9999999000000000000635\n  Bound result 18\n  Bound result 37\n  Bound result 9999999000000000008880\n  Bound result 10000000000000000\n  Bound result 5\n  Bound result 39062500000000\n  Bound result 61166851444257674191429\n  Bound result 5676\n  Bound result 39692058013454131539810\n  Bound result 12500\n  Bound result 3083\n  Bound result 70\n  Bound result 652\n  Bound result 87994341158995389561748\n  Bound result 5998\n  Bound result 1017108792\n  Bound result 162\n  Bound result 22\n  Bound result 9999999000000000000012\n  Bound result 7\n  Bound result 92\n  Bound result 9999999000000000002049\n  Bound result 95\n  Bound result 100\n  Bound result 9999999000000000000007\n  Bound result 43\n  Bound result 2\n  Bound result 4999999999999995000\n  Bound result 9\n  Bound result 512\n  Bound result 1\n  Bound result 200\n  Bound result 100\n  Bound result 1000000000000000\n  Bound result 96591368364614749421679\n  Bound result 4000\n  Bound result 3\n  Bound result 1043\n  Bound result 1\n  Bound result 73\n  Bound result 9999999899999990\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.58s (1.47s CPU time)\n\nRan 9 test suites in 1.59s (6.82s CPU time): 88 tests passed, 0 failed, 0 skipped (88 total tests)\n","passed":true},{"durationMs":243,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IMDBHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"IMDBHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"IMDBHook.processFees()\",\"IMDBHook.quoteSwap((address,address,uint24,int24,address),(bool,int256,uint160))\",\"IMDBHook.unlockCallback(bytes)\",\"IMDBToken.approve(address,uint256)\",\"IMDBToken.burn(uint256)\",\"IMDBToken.transfer(address,uint256)\",\"IMDBToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":5,\"LICENSE\":21,\"README.md\":89,\"checks/MainnetPairFork.sol.example\":53,\"docs/HOOK.md\":68,\"docs/OPERATIONS.md\":81,\"docs/SECURITY.md\":86,\"docs/dependencies.json\":152,\"foundry.toml\":22,\"package.json\":11,\"release/checksums.txt\":1,\"release/cid.txt\":1,\"release/imdbundler.car\":1040,\"remappings.txt\":4,\"scripts/configure-site.mjs\":10,\"scripts/draw-frogs.py\":43,\"scripts/mine-hook.mjs\":23,\"scripts/package-site.mjs\":72,\"scripts/serve.mjs\":19,\"site/LICENSE.txt\":21,\"site/_headers\":6,\"site/app.js\":369,\"site/assets/LICENSE.txt\":6,\"site/assets/pepe-dancing.svg\":7,\"site/assets/pepe-focused.svg\":7,\"site/assets/pepe-happy.svg\":7,\"site/assets/pepe-sad.svg\":7,\"site/assets/pepe-sleepy.svg\":7,\"site/assets/pepe-smug.svg\":7,\"site/assets/pepe-sunglasses.svg\":7,\"site/assets/pepe-thinking.svg\":7,\"site/chains.js\":12,\"site/deployment.json\":4,\"site/index.html\":122,\"site/rpc.js\":54,\"site/style.css\":8,\"site/transactions.js\":75,\"site/vault.js\":98,\"site/vendor/ethers.LICENSE.md\":21,\"site/vendor/ethers.NOTICES.txt\":145,\"site/vendor/ethers.js\":1,\"src/HookBase.sol\":85,\"src/HookFlags.sol\":29,\"src/IMDBHook.sol\":260,\"src/IMDBToken.sol\":18,\"test/HookInvariant.t.sol\":87,\"test/HookStatefulInvariant.t.sol\":267,\"test/IMDBHook.t.sol\":350,\"test/IMDBHookEdges.t.sol\":365,\"test/IMDBToken.t.sol\":70,\"test/mocks/BatchRouter.sol\":86,\"test/mocks/MockERC20.sol\":13,\"test/mocks/ReentryProbeManager.sol\":33,\"test/mocks/TestRouter.sol\":62,\"test/site/core.test.mjs\":164,\"test/site/dom.test.mjs\":100,\"test/site/release.test.mjs\":96,\"test/vendor/linkedom.LICENSE\":15,\"test/vendor/linkedom.NOTICES.txt\":248,\"test/vendor/linkedom.js\":12761},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"f24803d32ec05ae984379a1f7e5c8a8e757369cbfd4dfa0e5ec2efaebda757e0","verifiedTreeHash":"a4b826a8ee7ee61e3df1e59ae538a070d115997b","verifierVersion":"0.1.0+6a374fcd"}]}