{"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":"d929e590-1c91-448f-ab52-8f3097741578","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"6df6b7c717ad76e3d2b1500f3cf5e2859aac4b4cde6c2dda6537c50268c95939","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"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":"cf3616e2d6467a76c4d1b64d7d7266d6fde6b9fb96e107e4c9144d493c1f65e5","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"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":"77d266dce52cce2ad57934316cda1d83f3d52536cf2ae5fbaaab625358c75903","dependsOn":["build_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"3014f1ea5961918ca059453a484bf4c8bcbbfc2248dbe31d94ac7c5cdf8f50bd","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"3014f1ea5961918ca059453a484bf4c8bcbbfc2248dbe31d94ac7c5cdf8f50bd","skillId":"audit-judge","state":"accepted"},{"acceptedSubmissionHash":"0f7134b601ed0985cdae8f59fcb87288ed76c18cb3f2d167d0a3df0f978b3076","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"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":"75f3943de96e57347647beca1bb6a12586288477492ab5b5cb6d1fcd99acdd11","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"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":"42bb6187237a6942ed38b71b89bd506223ab178de7531a712ed7d75ecd45e85b","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"587567a5ef71546b5973ceaf6ef1dbbd590f49503b5ac9a6973ed86b89584857","skillId":"build-contract-project","tools":[]},"key":"build_contract_project","kind":"code","role":"implement","skillHash":"587567a5ef71546b5973ceaf6ef1dbbd590f49503b5ac9a6973ed86b89584857","skillId":"build-contract-project","state":"accepted"},{"acceptedSubmissionHash":"a5b17f40dee1a4337d9e1c097cf5a11b3829803a363cc05546cab6c449e02d09","dependsOn":["build_contract_project","write_foundry_tests"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"377196a274179129f153947115e1951654906b9dc1c91a17fae5fb95a5f6d500","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","state":"accepted"}],"objective":"[SIMD-LAUNCH]\nToken name: SIMDTEST\nToken symbol: SIMDTEST\n\nSIMDTESTHook (the launch pool's hook) implements a fixed 1% fee (100 bps) taken after every swap from the actual BalanceDelta of the unspecified currency (afterSwapReturnDelta). This fee applies on both buys and sells. When the fee is taken in launched tokens, it accrues inside the hook until anyone calls sweep(), which burns the tokens to 0x000000000000000000000000000000000000dEaD immediately. When the fee accrues in paired currency (IMD), it is held by the hook without being spent inside the swap callback.\n\nMechanics specification (authoritative: where anything above differs, follow these exactly):\nM1. Buyback and burn in batches: on EVERY swap (buys and sells) the hook takes an extra 1% in afterSwap from the actual BalanceDelta, on the unspecified currency (afterSwapReturnDelta), so the fee always matches what filled. On a buy that currency is usually the launched token; on a sell it is usually the paired currency. Launched-token fees are burned: an anyone-callable sweep() sends them to 0x000000000000000000000000000000000000dEaD. Paired-currency fees accrue in the hook to fund buybacks and are never spent inside a swap callback. executeBatch(), callable by anyone in its own transaction, runs at most once every 3600 seconds; its budget is up to 25% of the accrued paired-currency balance, and it swaps exact-input through the PoolManager (unlock and swap) with a sqrtPriceLimit 300 bps beyond a time-weighted reference price kept by the hook, accepting a partial fill: whatever does not fit inside that limit stays accrued for the next batch, so the buyback can never deadlock. The price limit is the only slippage guard (no minimum-output check that hardcodes the LP fee). Batch swaps are made by the hook itself and pay no hook fee. Bought tokens go to 0x000000000000000000000000000000000000dEaD. Immutable constants; views pending(), pendingBurn(), lastBatch(), referencePrice().\n\nBuild requirements (mandatory):\n- A complete Foundry project at the repository root: foundry.toml with solc 0.8.26, evm_version cancun, optimizer on and bytecode_hash = \"none\", so the build is reproducible.\n- Contracts: SIMDTESTHook. The hook is the hook of this launch's pool; keep its creation code within the EIP-3860 size limit.\n- No selfdestruct and no delegatecall anywhere in runtime code. No proxies, no owner, no upgradeability.\n- Chain: Ethereum mainnet (chainId 1). Uniswap v4 PoolManager: 0x000000000004444c5dc75cB358380D2e3dE08A90 (pass it to the hook constructor).\n- Paired currency: IMD, the ERC-20 at 0xd34a99bc0f67ae1bbd63c660e6d0b0dd03e263b7 on Ethereum mainnet (18 decimals).\n- Every address the hook needs is known now and fixed at deployment; nothing may require an owner or a setter after launch.\n- Supply distribution is done by the launch factory: it mints the supply, seeds the pool, sends the swarm's 10% through its Merkle distributor and any remainder to remainderTo. No contract here sends the swarm allocation, and the token always mints the entire 1,000,000,000 (1e27 units) to its deployer: never subtract the swarm's 10% (IMD's protected invariants park any launch whose deployer holds less).\n- Hook fees are collected through beforeSwap/afterSwap return deltas, on top of the pool's static 1.25% LP fee (fee tier 12500). Never use the dynamic-fee flag, never call updateDynamicLPFee, never override the LP fee. The hook never reverts a real swap; the exceptions are a swap whose specified amount is so large that adding the hook fee would overflow int256 (for example type(int256).max requests): it may revert with UnrepresentableFee. That is the accepted swap domain.\n- The hook is a plain immutable contract deployed directly at a CREATE2-mined address with the right permission bits, and launch.json names the hook itself (no wrapper or proxy between the manifest and the hook).\n- Tests: Foundry unit, fuzz and mainnet-fork tests that swap through the real PoolManager with the hook (exact-input and exact-output, buys and sells), plus permission bits matching the hook address.\n- Every hook fee is proportional to what actually filled. Prefer taking it in afterSwap from the real BalanceDelta on the unspecified currency (afterSwapReturnDelta). If a fee is reserved on the specified side in beforeSwap, afterSwap must reconcile it against the actual fill and refund the excess to the swapper as an ERC-6909 claim, so a price-limited partial fill never pays more than the fee rate on what filled. Test exact-input and exact-output partial fills with a price limit.\n- launch.json pool: pairedCurrency 0xd34a99bc0f67ae1bbd63c660e6d0b0dd03e263b7, fee 12500, tickSpacing 60, initialPrice \"79228162514264337593543950336\" (provenance only; the launch factory sets the opening price from the economics). Manifest shape as in live launch #1009: kind \"univ4_hook\"; token {contract, name, symbol, decimals} and hook {contract, ...} where each contract is a bare Solidity contract name like \"SIMDTEST\" or \"SIMDTESTHook\" (never a path or \"File.sol:Name\"); hook {contract, constructorArgs (e.g. [\"$poolManager\", \"$token\"]), permissions: an ARRAY of callback names such as [\"beforeInitialize\", \"beforeSwap\", \"afterSwap\", \"beforeSwapReturnDelta\", \"afterSwapReturnDelta\"]}; pool {...}; notes: a string explaining constructor args, permission bits and fees.","parentJobId":null,"planHash":"b1c7cde83738dfea78739d3d653a164356fc53455fe7c0ca5f151a160bf5cdab","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"d929e590-1c91-448f-ab52-8f3097741578","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-1021-simdtest"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51227","feedbackHash":"ffe0321f8585bdb2ac7b9cebe8608ed2226f21fc9e8ac9a5f912cb76fd60aa8a","nodeKey":"audit_economics","submissionHash":"6df6b7c717ad76e3d2b1500f3cf5e2859aac4b4cde6c2dda6537c50268c95939","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51142","feedbackHash":"bfda77acc11a713f606c6e42039b8237cb8845778258eb90466d056187f63cc0","nodeKey":"audit_flow","submissionHash":"cf3616e2d6467a76c4d1b64d7d7266d6fde6b9fb96e107e4c9144d493c1f65e5","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51473","feedbackHash":"4428fe1948946fcf8bf538e1164557dae1e3f23ce3855ab2e9641975b13daf1c","nodeKey":"audit_judge","submissionHash":"77d266dce52cce2ad57934316cda1d83f3d52536cf2ae5fbaaab625358c75903","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52223","feedbackHash":"0069b45fa2d594d2c4180c0bbef35e8f50aa47137596fb4585ebad3ce48d7700","nodeKey":"audit_judge","submissionHash":"5f48a916e6f66cc2b11bbcb3d6376a0bc108ed5547251fd21f0a318d02d7dc2d","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51166","feedbackHash":"6d62c57f152ededd81d5d7f53097980ca1286f80336b5e8a160d6177be41bcfd","nodeKey":"audit_math","submissionHash":"0f7134b601ed0985cdae8f59fcb87288ed76c18cb3f2d167d0a3df0f978b3076","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51143","feedbackHash":"633940660cbe5f1222972d356f003a37b06051d3a629c034accc92016ff21c4e","nodeKey":"audit_permissions","submissionHash":"75f3943de96e57347647beca1bb6a12586288477492ab5b5cb6d1fcd99acdd11","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51237","feedbackHash":"a29afbce112003053029a49f1f1ec53d2677b64835b36bbc4cdec683b1da0425","nodeKey":"build_contract_project","submissionHash":"de34b0cffb20bc3a2e03c4bd2cbcfae1754a615af284b558cf7b65fa5436be88","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51006","feedbackHash":"478c0bf3b41834515dda3e0113a18e6d1a4fd2f8a6160a1f7791d72b90830c4b","nodeKey":"build_contract_project","submissionHash":"42bb6187237a6942ed38b71b89bd506223ab178de7531a712ed7d75ecd45e85b","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"50967","feedbackHash":"5025b5c1069647c882c7cb337d0e065a476befcfda1b242866f0c9fc55e880ba","nodeKey":"manifest","submissionHash":"a01c5c6b983eff34791034fbcfbeadcc8df90b47da160cca0e819b5b4ef5d9ff","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51067","feedbackHash":"ee7146daea8377decae34a1120a34103634d644874b3b27fed004054152e3fd4","nodeKey":"manifest","submissionHash":"a5b17f40dee1a4337d9e1c097cf5a11b3829803a363cc05546cab6c449e02d09","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51089","feedbackHash":"301b785dfabf21680b287abc5331a14d35294a6c74890bfcd5a66bf1cfe43d0a","nodeKey":"write_foundry_tests","submissionHash":"377196a274179129f153947115e1951654906b9dc1c91a17fae5fb95a5f6d500","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51001","feedbackHash":"e7e25ed542856788d0474f1e7216a001186487078553143b0d324f2814c8a02d","nodeKey":"write_foundry_tests","submissionHash":"4d5fdff76b9c5856dfbf0f4e564fc252d2f553c2dd174bac5e6419077f8b42d3","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"adc7b84372d0774902564e555bf55d372f0489e2693e7adf275ee21ced5b48f7","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"45aa937328087de3","findings":[{"citation":"resolved","description":"_observe() (called from afterSwap for every external swap, including swaps that filled nothing) stores slot0.tick as the tick to be time-weighted into referencePrice(). In Uniswap v4 a swap that enters a region with no active liquidity moves sqrtPriceX96 to its sqrtPriceLimitX96 with zero amounts in and out, so anyone can place spot at MIN_SQRT_PRICE+1 (or MAX_SQRT_PRICE-1) at the cost of gas whenever the side of the pool they swap toward is empty. That untraded tick then carries full time weight: after an hour the reference is tick -887026 (price 4348282389 Q96) for a pool whose real liquidity sits at ticks 60..600. executeBatch() computes limitX96 from that reference, so its swap runs from MIN+1 only up to the garbage limit, fills nothing, burns the hourly slot (lastBatch = now) and resets the epoch at the garbage post-batch tick (-886731), so the following epoch starts poisoned as well unless a real trade restores the price. Preconditions: an empty region adjacent to spot. That is the state of a token-only seeded launch pool before/at its first buys (the project's own FreshManagerTest models exactly this seeding), and it is reachable later by selling tokens until the pool's IMD reserve is exhausted and leaving the price there (round-trip cost about 4.5% of the reserve in LP+hook fees plus price exposure, measured 43.98 tokens on a 1000 IMD reserve). The manipulation can only push the reference toward 'token worthless', so the hook is never made to overpay; the impact is that buybacks are skipped for an epoch per manipulation while fees keep accruing, a liveness failure of the batch mechanism rather than a loss of funds. Seam: boundary (zero-liquidity price jump, zero fill) x invariant (reference reflects traded prices). Suggested direction: do not adopt a post-swap tick as an observation when poolManager.getLiquidity(poolId) == 0 (keep the previous observed tick), or skip observations for swaps whose BalanceDelta is zero on both sides; the proof passes with the former. Also consider clamping the reference to a band around the last traded tick.","line":248,"path":"src/SIMDTESTHook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {SIMDTEST} from \"src/SIMDTEST.sol\";\nimport {SIMDTESTHook} from \"src/SIMDTESTHook.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 {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 {StateLibrary} from \"v4-core/src/libraries/StateLibrary.sol\";\nimport {TickMath} from \"v4-core/src/libraries/TickMath.sol\";\n\ncontract MiniERC20 {\n    string public name = \"IMD fixture\";\n    string public symbol = \"IMD\";\n    uint8 public decimals = 18;\n    uint256 public totalSupply;\n    mapping(address => uint256) public balanceOf;\n    mapping(address => mapping(address => uint256)) public allowance;\n\n    function mint(address to, uint256 amount) external {\n        totalSupply += amount;\n        balanceOf[to] += amount;\n    }\n\n    function approve(address spender, uint256 amount) external returns (bool) {\n        allowance[msg.sender][spender] = amount;\n        return true;\n    }\n\n    function transfer(address to, uint256 amount) external returns (bool) {\n        balanceOf[msg.sender] -= amount;\n        balanceOf[to] += amount;\n        return true;\n    }\n\n    function transferFrom(address from, address to, uint256 amount) external returns (bool) {\n        if (allowance[from][msg.sender] != type(uint256).max) allowance[from][msg.sender] -= amount;\n        balanceOf[from] -= amount;\n        balanceOf[to] += amount;\n        return true;\n    }\n}\n\ninterface IERC20Min {\n    function transferFrom(address, address, uint256) external returns (bool);\n    function approve(address, uint256) external returns (bool);\n    function transfer(address, uint256) external returns (bool);\n    function balanceOf(address) external view returns (uint256);\n}\n\n/// @dev Minimal router: swap or add liquidity, settling with the payer's ERC-20 approvals.\ncontract Router is IUnlockCallback {\n    IPoolManager immutable manager;\n\n    constructor(IPoolManager m) {\n        manager = m;\n    }\n\n    function swap(PoolKey memory key, SwapParams memory p) external returns (BalanceDelta) {\n        return abi.decode(manager.unlock(abi.encode(true, msg.sender, key, abi.encode(p))), (BalanceDelta));\n    }\n\n    function liquidity(PoolKey memory key, int24 lo, int24 hi, int256 amount) external {\n        manager.unlock(abi.encode(false, msg.sender, key, abi.encode(ModifyLiquidityParams(lo, hi, amount, 0))));\n    }\n\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        require(msg.sender == address(manager));\n        (bool isSwap, address payer, PoolKey memory key, bytes memory payload) =\n            abi.decode(data, (bool, address, PoolKey, bytes));\n        BalanceDelta d;\n        if (isSwap) d = manager.swap(key, abi.decode(payload, (SwapParams)), \"\");\n        else (d,) = manager.modifyLiquidity(key, abi.decode(payload, (ModifyLiquidityParams)), \"\");\n        _settle(key.currency0, d.amount0(), payer);\n        _settle(key.currency1, d.amount1(), payer);\n        return abi.encode(d);\n    }\n\n    function _settle(Currency c, int128 delta, address payer) private {\n        if (delta < 0) {\n            manager.sync(c);\n            require(IERC20Min(Currency.unwrap(c)).transferFrom(payer, address(manager), uint256(-int256(delta))));\n            manager.settle();\n        } else if (delta > 0) {\n            manager.take(c, payer, uint128(delta));\n        }\n    }\n}\n\n/// @notice A zero-fill swap through an empty liquidity region moves spot to the price limit at no\n///         cost. The hook records that untraded tick in its time-weighted reference, and the next\n///         batch therefore spends nothing although the pool still offers tokens at the true price.\ncontract ZeroFillReferenceProof is Test {\n    using StateLibrary for IPoolManager;\n\n    address constant IMD = 0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7;\n    uint160 constant Q96 = 79228162514264337593543950336;\n\n    IPoolManager manager;\n    SIMDTEST token;\n    SIMDTESTHook hook;\n    Router router;\n    PoolKey key;\n\n    function setUp() public {\n        manager = IPoolManager(address(new PoolManager(address(this))));\n        vm.etch(IMD, address(new MiniERC20()).code);\n        MiniERC20(IMD).mint(address(this), 10_000_000 ether);\n\n        // Launch token below IMD so the token is currency0 (the IMD-is-currency1 ordering).\n        bytes32 tokenHash = keccak256(type(SIMDTEST).creationCode);\n        for (uint256 i;; ++i) {\n            address predicted = address(\n                uint160(uint256(keccak256(abi.encodePacked(hex\"ff\", address(this), bytes32(i), tokenHash))))\n            );\n            if (predicted < IMD) {\n                token = new SIMDTEST{salt: bytes32(i)}();\n                break;\n            }\n        }\n        bytes32 hookHash =\n            keccak256(abi.encodePacked(type(SIMDTESTHook).creationCode, abi.encode(manager, address(token))));\n        for (uint256 i;; ++i) {\n            address predicted = address(\n                uint160(uint256(keccak256(abi.encodePacked(hex\"ff\", address(this), bytes32(i), hookHash))))\n            );\n            if ((uint160(predicted) & 0x3fff) == 0x20c4) {\n                hook = new SIMDTESTHook{salt: bytes32(i)}(manager, address(token));\n                break;\n            }\n        }\n        key = hook.poolKey();\n        assertFalse(hook.pairedIsCurrency0());\n        router = new Router(manager);\n        token.approve(address(router), type(uint256).max);\n        IERC20Min(IMD).approve(address(router), type(uint256).max);\n\n        manager.initialize(key, Q96);\n        // Token-only seed: a position entirely above spot holds only currency0 (the launch token).\n        router.liquidity(key, 60, 600, 10_000_000 ether);\n        assertEq(IERC20Min(IMD).balanceOf(address(manager)), 0);\n        // Buyback funds already accrued to the hook.\n        IERC20Min(IMD).transfer(address(hook), 1000 ether);\n    }\n\n    function test_zeroFillSwapDoesNotStallTheBuyback() public {\n        vm.warp(block.timestamp + 1);\n        uint256 tokBefore = token.balanceOf(address(this));\n        uint256 imdBefore = IERC20Min(IMD).balanceOf(address(this));\n\n        // Attacker: sell 1 wei of token toward MIN price. Nothing below spot is liquid: fill is zero.\n        BalanceDelta d = router.swap(key, SwapParams(true, -1, TickMath.MIN_SQRT_PRICE + 1));\n        assertEq(d.amount0(), 0);\n        assertEq(d.amount1(), 0);\n        assertEq(token.balanceOf(address(this)), tokBefore, \"attacker paid nothing\");\n        assertEq(IERC20Min(IMD).balanceOf(address(this)), imdBefore);\n        (uint160 spot,,,) = manager.getSlot0(key.toId());\n        assertEq(spot, TickMath.MIN_SQRT_PRICE + 1, \"spot moved to the limit for free\");\n\n        vm.warp(block.timestamp + 3599);\n        uint256 pendingBefore = hook.pending();\n        (uint256 spent, uint256 burned) = hook.executeBatch();\n        emit log_named_int(\"reference tick used\", TickMath.getTickAtSqrtPrice(hook.referencePrice()));\n        emit log_named_uint(\"spent\", spent);\n        emit log_named_uint(\"burned\", burned);\n\n        // Tokens are still on offer from tick 60 upward at the true price; a reference that only\n        // reflects traded prices lets the batch buy them. On the current code spent == 0.\n        assertGt(spent, 0, \"batch stalled by an untraded reference tick\");\n        assertGt(burned, 0);\n        assertEq(hook.pending(), pendingBefore - spent);\n    }\n}","reproduction":"Local PoolManager, SIMDTEST deployed below IMD (token = currency0, pairedIsCurrency0 = false), hook mined with flags 0x20c4, initialize at sqrtPrice 2^96 (tick 0); add liquidity [60,600] with 1e25 liquidity (token-only, manager holds 0 IMD); transfer 1000e18 IMD to the hook (pending = 1000e18). Control: warp +3600, executeBatch() -> spent 250e18, burned 245.39e18. Attack: warp +1; any account calls swap(zeroForOne=true, amountSpecified=-1, sqrtPriceLimitX96=MIN_SQRT_PRICE+1): BalanceDelta (0,0), attacker balances unchanged, slot0.sqrtPriceX96 == 4295128740 (tick -887272). Warp +3599: referencePrice() = 4348282389 (tick -887026). executeBatch() -> spent 0, burned 0, pending still 1000e18, lastBatch = now, post-batch tick -886731 and referencePrice() now equals that tick's price (next epoch poisoned). Expected: the batch buys the tokens still offered from tick 60 up to the 300 bps band around the traded price (spent > 0 as in the control). Proof: test/scratch/ZeroFillReferenceProof.t.sol fails on the current code ('batch stalled by an untraded reference tick: 0 <= 0') and passes on a copy of the hook whose _observe keeps the previous tick when getLiquidity(poolId) == 0.","severity":"medium","snippet":"        (, observedTick,,) = poolManager.getSlot0(poolId);","title":"Time-weighted reference records ticks reached through zero-liquidity regions, so a zero-fill swap stalls the hourly buyback"},{"citation":"resolved","description":"The fee is floor(|unspecified delta| / 100). The Math Precision guide's convention is that fees round up; here every fill pays at most 1% and dust fills pay 0. With 18-decimal currencies the shortfall is at most 99 wei per swap and gas makes splitting a trade into sub-100-wei pieces absurd, so there is no exploitable economic effect; the README documents the floor. Reported for completeness of the rounding-direction review, no action strictly required. If the author prefers the conventional direction, fee = (abs + 99) / 100 with abs > 0 keeps abs(int128.min) in range as well.","line":149,"path":"src/SIMDTESTHook.sol","reproduction":"Seeded pool (liquidity [-600,600], 1e25). Exact-output buy of 199 wei token: gross delta (token +199, IMD -205), fee = 205/100 = 2 wei (0.976%). Exact-input sell whose IMD output is 99 wei: fee 0; output 199 wei: fee 1 wei (0.503%). Existing test_DustSwapsAroundOnePercentRoundingBoundary shows amounts 1..99 pay 0 fee.","severity":"info","snippet":"        uint256 amount = uint256(filled < 0 ? -filled : filled) / 100;","title":"Hook fee rounds down, so fills below 100 wei pay nothing and small fills pay less than 1%"},{"citation":"resolved","description":"referencePrice() returns getSqrtPriceAtTick(floor(meanTick)); flooring lowers the reference by up to one tick (0.01% in price). In addition, after a zeroForOne swap that stops exactly on an initialized tick, core stores slot0.tick = tickNext - 1 although the price equals getSqrtPriceAtTick(tickNext), so an observed tick can be one below the price tick. When IMD is currency0 the batch swaps zeroForOne and its limit is reference * sqrt(0.97) (rounded up by at most 1 wei of sqrt price, which does not compensate a whole tick), so the pool price the hook may push to is up to 0.97 * 1.0001^-2 of the true geometric mean, i.e. about 302 bps instead of 300. When the token is currency0 both effects tighten the band instead. Negligible against the 300 bps tolerance and the 1.25% LP fee that the price limit already does not account for; noted so the asymmetry is a known choice. Rounding the mean toward the spot-protecting side per ordering (ceil when pairedIsCurrency0) would remove it.","line":182,"path":"src/SIMDTESTHook.sol","reproduction":"IMD < token (pairedIsCurrency0 = true), epoch of 3600 s with tick 0 for 1800 s and tick -1 for 1800 s: sum = -1800, mean = -0.5, floor -> -1; referencePrice = getSqrtPriceAtTick(-1) = 79224201403219477170569942573 instead of the price at -0.5. limitX96 = mulDivRoundingUp(ref, 984885780179610473, 1e18); squared ratio to the true mean price = 0.97 * 1.0001^-1 = 0.969903, i.e. 300.97 bps. With the tickNext-1 convention adding another tick: 0.969806 (301.9 bps).","severity":"info","snippet":"        return TickMath.getSqrtPriceAtTick(int24(mean));","title":"Reference tick is floored and slot0.tick can sit one below the price tick, so the 300 bps band is up to about 2 bps wider in the IMD-is-currency0 ordering"}],"hash":"0f7134b601ed0985cdae8f59fcb87288ed76c18cb3f2d167d0a3df0f978b3076","nodeId":"751afaf0-ef73-4e12-a1f0-a37be549c920","outcome":"completed","summary":"The review is complete and the findings file is in place.\n\n**Outcome.** The hook's arithmetic holds up well under the Math Precision, Boundary and Numerical Gap checks. I found one medium defect at the seam between a boundary and an invariant, plus two informational rounding notes. No funds can be lost through anything in my area.\n\n**Finding 1, medium.** The time-weighted reference adopts whatever tick a swap leaves in slot0, including ticks reached through a region with no liquidity. In v4 such a swap moves the price to its limit with zero amounts, so anyone can park spot at the minimum price for gas alone while the side they swap toward is empty. After an hour the reference reads tick -887026 for a pool whose liquidity sits at ticks 60 to 600. The batch then spends nothing, burns its hourly slot, and resets the epoch at the poisoned tick. The manipulation only ever pushes the reference toward \"token worthless\", so the hook never overpays. The impact is stalled buybacks, one epoch per manipulation. It is free in the token-only seeded phase and costs roughly 4.5% of the pool's IMD reserve later. A self-contained proof at `test/scratch/ZeroFillReferenceProof.t.sol` fails on the current code and passes when the observation is skipped while pool liquidity is zero.\n\n**Informational.** The fee floors to whole wei, so fills under 100 wei pay nothing and a 199 wei fill pays 0.5%. Flooring the mean tick, together with core's tick-minus-one convention, widens the 300 bps band by about 2 bps in the IMD-is-currency0 ordering only.\n\n**Verified as sound.** The sqrt factors are exactly ceil(sqrt(0.97)·1e18) and floor(sqrt(1.03)·1e18). Fee selection of the unspecified side, the int128 and int24 casts, the room check against core's price-limit comparator, the claims-first settlement split, and delta conservation all hold. I also fetched IMD's mainnet bytecode: it is a LayerZero OFT on a standard ERC-20 with no pause, blocklist or transfer-fee functions.\n\n**Coverage.** All 8 entry points have rows. The only unreached item is the mainnet fork rehearsal, which needs an archival RPC this worker does not have.","treeHash":null,"usage":{"cachedInputTokens":1813806,"inputTokens":386,"model":"claude-fable-5-1","outputTokens":69494,"runtime":"claude","turns":36,"wallClockMs":984927}},{"artifacts":[],"attempt":1,"bundleHash":"be43e4b9fa6f3f3cee7c412d03e0fc9a5bd4be7dc4a91156a6d129ff116f7875","device":"393ade31b99d5d5b","findings":[],"hash":"377196a274179129f153947115e1951654906b9dc1c91a17fae5fb95a5f6d500","nodeId":"5f7e1fef-65e7-4e5c-9f82-49af7f3e5409","outcome":"completed","summary":"Updated the existing tests for empty-batch cooldown and TWAP preservation, with deterministic funded-retry coverage. Changes are confined to `test/`.\n\nValidation passed:\n\n- `forge build`\n- 102 offline tests; two explicit fork skips\n- 56 mainnet-fork tests at block 26,146,258\n- Both invariant suites: 16,384 calls each, zero unexpected reverts\n\nNo new implementation defects were identified.","treeHash":"34340478e25309e1277e6f84a469395d80b15f23","usage":{"cachedInputTokens":941696,"inputTokens":72091,"model":"gpt-6-astra","outputTokens":5550,"runtime":"codex","turns":4,"wallClockMs":238541}},{"artifacts":[],"attempt":1,"bundleHash":"083156b30010acbe64ace9fac3bab20c28f7144014da5cfe58eb54dbb806dcfe","device":"dca8f60f0d611b2d","findings":[],"hash":"42bb6187237a6942ed38b71b89bd506223ab178de7531a712ed7d75ecd45e85b","nodeId":"8bb012d3-ee80-4328-9349-a2579b0ddb85","outcome":"completed","summary":"Fixed both medium findings and the zero-fill cooldown grief. Added regression and fuzz coverage; documented all eight verdicts in [.imd-responses.json](/home/imd-worker/.identitymd/work/d929e590-1c91-448f-ab52-8f3097741578/8bb012d3-ee80-4328-9349-a2579b0ddb85/.imd-responses.json).\n\nValidation: `forge build` and `forge fmt --check` pass; `forge test` reports 74 passed, zero failed. The supplied proof also passes. Mainnet-fork testing remains skipped without an RPC.","treeHash":"f825a4b94de6cb7c41dd088e638119ec5af3ab88","usage":{"cachedInputTokens":1365504,"inputTokens":111799,"model":"gpt-6-astra","outputTokens":18590,"runtime":"codex","turns":6,"wallClockMs":779556}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"4f6165fcd0deb045","findings":[],"hash":"45458ff9aa3479f87c1f484e1459ac70b6f0f35b48349eeace511f4d1957d0ec","nodeId":"1c1195cf-dc50-4b5a-a49e-b7dad867fee9","outcome":"failed","summary":"could not restore accepted work: accepted work conflicts with a dependency, which plan validation should have prevented: ","treeHash":null,"usage":{"cachedInputTokens":0,"inputTokens":0,"model":null,"outputTokens":0,"runtime":"codex","turns":0,"wallClockMs":1024}},{"artifacts":[],"attempt":1,"bundleHash":"dea928e77814ef288340f2abce1c60b2783bd35d30c62c51918c5e8e8f8832c4","device":"6156e0902980b6f9","findings":[],"hash":"4d5fdff76b9c5856dfbf0f4e564fc252d2f553c2dd174bac5e6419077f8b42d3","nodeId":"5f7e1fef-65e7-4e5c-9f82-49af7f3e5409","outcome":"completed","summary":"Completed entirely under `test/`, adding failure-path, fuzz, and stronger accounting invariants.\n\n- `forge build` passed.\n- Offline tests: 68 passed; 2 fork suites skipped.\n- Mainnet fork: 56 passed at block 26,146,258.\n- Invariants: 32,768 calls with no unexpected reverts.\n\nNo reproducible implementation defects found. Run commands and coverage are documented in `test/README.md`.","treeHash":"bceeb3a5cbcf285082362d117e397924f86839f5","usage":{"cachedInputTokens":1593472,"inputTokens":100777,"model":"gpt-6-astra","outputTokens":22469,"runtime":"codex","turns":6,"wallClockMs":875412}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"f20ec62b0213db3f","findings":[{"citation":"resolved","description":"_observe() (called from afterSwap on every external swap, including swaps that filled nothing) records whatever slot0.tick the PoolManager left, with no regard to whether any liquidity exists there. In Uniswap v4, a swap toward a region with no active liquidity fills nothing but still walks sqrtPriceX96 to the caller's sqrtPriceLimitX96. The launch pool is seeded with launched tokens only (a one-sided position on one side of the opening price), so the sell side of the opening price is empty until buyers have put IMD in, and it empties again whenever sellers drain the IMD. In that state a 1-wei sell with the router-default limit (MIN_SQRT_PRICE+1 or MAX_SQRT_PRICE-1) moves slot0.tick to -887272 / +887271 for gas only: BalanceDelta is (0,0), no hook fee, no LP fee. The hook then weights that tick by elapsed time: after 12 seconds of a 3600-second epoch the mean tick is already about -2958 (26% below a spot that sat at tick 0 for the other 3588 seconds); after a full hour it is -887026. executeBatch() derives limitX96 from that reference, so either `room` is false (budget unspent, lastBatch advanced, hourly slot consumed, epoch reset) or, if spot is still in the void, the hook's own exact-input swap fills zero while moving the price 300 bps toward the limit and re-anchors the next epoch in the void. The manipulation only ever pushes the reference toward 'token worthless', so the hook never overpays; the impact is liveness: every occurrence skips the buyback for the rest of that epoch plus the next hour, it costs nothing, it recurs whenever price returns to the launch floor, and an ordinary seller with a loose limit triggers it by accident. Merged from audit_flow and audit_math (same root cause). Suggested direction, author's call: do not adopt slot0.tick as an observation when poolManager.getLiquidity(poolId) == 0 (keep the previous tick), or skip observations for swaps whose BalanceDelta is zero on both sides; the attached proof passes with the former.","line":248,"path":"src/SIMDTESTHook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {SIMDTEST} from \"src/SIMDTEST.sol\";\nimport {SIMDTESTHook} from \"src/SIMDTESTHook.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 {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 {StateLibrary} from \"v4-core/src/libraries/StateLibrary.sol\";\nimport {TickMath} from \"v4-core/src/libraries/TickMath.sol\";\n\ncontract MiniERC20 {\n    string public name = \"IMD fixture\";\n    string public symbol = \"IMD\";\n    uint8 public decimals = 18;\n    uint256 public totalSupply;\n    mapping(address => uint256) public balanceOf;\n    mapping(address => mapping(address => uint256)) public allowance;\n\n    function mint(address to, uint256 amount) external {\n        totalSupply += amount;\n        balanceOf[to] += amount;\n    }\n\n    function approve(address spender, uint256 amount) external returns (bool) {\n        allowance[msg.sender][spender] = amount;\n        return true;\n    }\n\n    function transfer(address to, uint256 amount) external returns (bool) {\n        balanceOf[msg.sender] -= amount;\n        balanceOf[to] += amount;\n        return true;\n    }\n\n    function transferFrom(address from, address to, uint256 amount) external returns (bool) {\n        if (allowance[from][msg.sender] != type(uint256).max) allowance[from][msg.sender] -= amount;\n        balanceOf[from] -= amount;\n        balanceOf[to] += amount;\n        return true;\n    }\n}\n\ninterface IERC20Min {\n    function transferFrom(address, address, uint256) external returns (bool);\n    function approve(address, uint256) external returns (bool);\n    function transfer(address, uint256) external returns (bool);\n    function balanceOf(address) external view returns (uint256);\n}\n\n/// @dev Minimal router: swap or add liquidity, settling with the payer's ERC-20 approvals.\ncontract Router is IUnlockCallback {\n    IPoolManager immutable manager;\n\n    constructor(IPoolManager m) {\n        manager = m;\n    }\n\n    function swap(PoolKey memory key, SwapParams memory p) external returns (BalanceDelta) {\n        return abi.decode(manager.unlock(abi.encode(true, msg.sender, key, abi.encode(p))), (BalanceDelta));\n    }\n\n    function liquidity(PoolKey memory key, int24 lo, int24 hi, int256 amount) external {\n        manager.unlock(abi.encode(false, msg.sender, key, abi.encode(ModifyLiquidityParams(lo, hi, amount, 0))));\n    }\n\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        require(msg.sender == address(manager));\n        (bool isSwap, address payer, PoolKey memory key, bytes memory payload) =\n            abi.decode(data, (bool, address, PoolKey, bytes));\n        BalanceDelta d;\n        if (isSwap) d = manager.swap(key, abi.decode(payload, (SwapParams)), \"\");\n        else (d,) = manager.modifyLiquidity(key, abi.decode(payload, (ModifyLiquidityParams)), \"\");\n        _settle(key.currency0, d.amount0(), payer);\n        _settle(key.currency1, d.amount1(), payer);\n        return abi.encode(d);\n    }\n\n    function _settle(Currency c, int128 delta, address payer) private {\n        if (delta < 0) {\n            manager.sync(c);\n            require(IERC20Min(Currency.unwrap(c)).transferFrom(payer, address(manager), uint256(-int256(delta))));\n            manager.settle();\n        } else if (delta > 0) {\n            manager.take(c, payer, uint128(delta));\n        }\n    }\n}\n\n/// @notice A zero-fill swap through an empty liquidity region moves spot to the price limit at no\n///         cost. The hook records that untraded tick in its time-weighted reference, and the next\n///         batch therefore spends nothing although the pool still offers tokens at the true price.\ncontract ZeroFillReferenceProof is Test {\n    using StateLibrary for IPoolManager;\n\n    address constant IMD = 0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7;\n    uint160 constant Q96 = 79228162514264337593543950336;\n\n    IPoolManager manager;\n    SIMDTEST token;\n    SIMDTESTHook hook;\n    Router router;\n    PoolKey key;\n\n    function setUp() public {\n        manager = IPoolManager(address(new PoolManager(address(this))));\n        vm.etch(IMD, address(new MiniERC20()).code);\n        MiniERC20(IMD).mint(address(this), 10_000_000 ether);\n\n        // Launch token below IMD so the token is currency0 (the IMD-is-currency1 ordering).\n        bytes32 tokenHash = keccak256(type(SIMDTEST).creationCode);\n        for (uint256 i;; ++i) {\n            address predicted = address(\n                uint160(uint256(keccak256(abi.encodePacked(hex\"ff\", address(this), bytes32(i), tokenHash))))\n            );\n            if (predicted < IMD) {\n                token = new SIMDTEST{salt: bytes32(i)}();\n                break;\n            }\n        }\n        bytes32 hookHash =\n            keccak256(abi.encodePacked(type(SIMDTESTHook).creationCode, abi.encode(manager, address(token))));\n        for (uint256 i;; ++i) {\n            address predicted = address(\n                uint160(uint256(keccak256(abi.encodePacked(hex\"ff\", address(this), bytes32(i), hookHash))))\n            );\n            if ((uint160(predicted) & 0x3fff) == 0x20c4) {\n                hook = new SIMDTESTHook{salt: bytes32(i)}(manager, address(token));\n                break;\n            }\n        }\n        key = hook.poolKey();\n        assertFalse(hook.pairedIsCurrency0());\n        router = new Router(manager);\n        token.approve(address(router), type(uint256).max);\n        IERC20Min(IMD).approve(address(router), type(uint256).max);\n\n        manager.initialize(key, Q96);\n        // Token-only seed: a position entirely above spot holds only currency0 (the launch token).\n        router.liquidity(key, 60, 600, 10_000_000 ether);\n        assertEq(IERC20Min(IMD).balanceOf(address(manager)), 0);\n        // Buyback funds already accrued to the hook.\n        IERC20Min(IMD).transfer(address(hook), 1000 ether);\n    }\n\n    function test_zeroFillSwapDoesNotStallTheBuyback() public {\n        vm.warp(block.timestamp + 1);\n        uint256 tokBefore = token.balanceOf(address(this));\n        uint256 imdBefore = IERC20Min(IMD).balanceOf(address(this));\n\n        // Attacker: sell 1 wei of token toward MIN price. Nothing below spot is liquid: fill is zero.\n        BalanceDelta d = router.swap(key, SwapParams(true, -1, TickMath.MIN_SQRT_PRICE + 1));\n        assertEq(d.amount0(), 0);\n        assertEq(d.amount1(), 0);\n        assertEq(token.balanceOf(address(this)), tokBefore, \"attacker paid nothing\");\n        assertEq(IERC20Min(IMD).balanceOf(address(this)), imdBefore);\n        (uint160 spot,,,) = manager.getSlot0(key.toId());\n        assertEq(spot, TickMath.MIN_SQRT_PRICE + 1, \"spot moved to the limit for free\");\n\n        vm.warp(block.timestamp + 3599);\n        uint256 pendingBefore = hook.pending();\n        (uint256 spent, uint256 burned) = hook.executeBatch();\n        emit log_named_int(\"reference tick used\", TickMath.getTickAtSqrtPrice(hook.referencePrice()));\n        emit log_named_uint(\"spent\", spent);\n        emit log_named_uint(\"burned\", burned);\n\n        // Tokens are still on offer from tick 60 upward at the true price; a reference that only\n        // reflects traded prices lets the batch buy them. On the current code spent == 0.\n        assertGt(spent, 0, \"batch stalled by an untraded reference tick\");\n        assertGt(burned, 0);\n        assertEq(hook.pending(), pendingBefore - spent);\n    }\n}","reproduction":"Local PoolManager; SIMDTEST mined below IMD so token = currency0 (pairedIsCurrency0 == false); hook at a 0x20c4 address; initialize at sqrtPrice 2^96 (tick 0); add token-only liquidity in [60,600] with 1e25 units (manager holds 0 IMD); transfer 1000e18 IMD to the hook (pending() == 1000e18). Control: warp +3600 and call executeBatch() -> spent 250e18, burned ~245e18. Attack: warp +1; any account swaps zeroForOne, amountSpecified = -1, sqrtPriceLimitX96 = MIN_SQRT_PRICE+1 -> BalanceDelta (0,0), swapper balances unchanged, slot0.sqrtPriceX96 == 4295128740 (tick -887272). Warp +3599: referencePrice() == 4348282389 (tick -887026). executeBatch() -> returns (0, 0), pending() still 1000e18, lastBatch == now, next epoch opens at tick -886731. Expected: the batch buys the tokens still offered from tick 60 within 300 bps of the traded price (spent > 0 as in the control). Actual: spent == 0 and the hourly slot is consumed. Run: forge test --match-path test/scratch/Proof_6890a36a0319.t.sol -vv -> FAIL 'batch stalled by an untraded reference tick: 0 <= 0' (verified on this tree). The audit_flow reproduction (void excursion for 12 s, then a buyer restores spot, batch at +3600 spends 0 instead of 250e18) follows from the same arithmetic in both currency orderings.","severity":"medium","snippet":"        (, observedTick,,) = poolManager.getSlot0(poolId);","title":"A zero-fill swap through the empty side of the pool parks slot0.tick in a no-liquidity region and the hook time-weights that untraded tick into referencePrice(), stalling the hourly buyback at zero co"},{"citation":"resolved","description":"limitX96 is derived solely from referencePrice(), the time-weighted mean tick since the epoch started. Nothing relates it to the pool price at the moment the batch runs. When the price moved during the epoch in the direction that makes the launched token cheaper (c1/c0 rises when IMD is currency0), the reference lags spot and the limit sits far from spot, so the exact-input swap is allowed to move the pool by the whole lag plus 300 bps. Because executeBatch() is permissionless and the keeper chooses the timing, a caller can buy at the depressed price first, let the batch push the price up toward the stale reference, and sell back in the same block; the gain exceeds the 2.25% (1.25% LP + 1% hook) paid on each leg. Measured: 10M liquidity units in [-12000, 12000], 2,000,000 IMD pending (budget 500,000), market sells to tick 2000 at 3000 s into the epoch; at 3600 s the reference is tick 333 and the limit tick 28; the unsandwiched batch spends 502,379 IMD and moves the pool from tick 2000 to 932 (1068 ticks, about 10.1% in price) burning 574,436 tokens where about 613,600 would have been bought at the pre-batch spot. A front-run buy to tick 1900 nets the sandwicher 2,880 IMD after fees; a front-run to tick 1100 (421,729 IMD at risk) nets 24,133 IMD, 4.8% of the batch, paid out of the hook's accrued IMD (holders receive fewer burned tokens). With no drift the same sandwich loses 1,240 IMD, which shows the 300 bps band itself is safe and the lag is the defect. This follows the brief's literal definition (limit = 300 bps beyond the TWAP) and is therefore partly a design property of the requested mechanism; it is reported because the brief calls the limit a slippage guard and it does not guard against this. A fix that keeps the brief's TWAP anchor and its 'price limit is the only slippage guard' rule: use the tighter of (300 bps from the TWAP) and (300 bps from the pre-batch spot) as limitX96; with that change the batch moves the pool 299 bps and the sandwich loses money. Reported by audit_economics; reproduced here with an independent test.","line":200,"path":"src/SIMDTESTHook.sol","reproduction":"State: IMD = currency0, token = currency1, pool initialized at tick 0, liquidity 10,000,000e18 units over [-12000, 12000], 2,000,000e18 IMD transferred to the hook. (1) warp +3000 s; a market seller swaps oneForZero exact-input with sqrtPriceLimit = getSqrtPriceAtTick(2000), leaving spot at tick 2000. (2) warp +600 s. referencePrice() corresponds to tick 333. (3a) executeBatch() alone: spent 502378951451680778047928, burned 574436180868452776143749, slot0.tick 2000 -> 932. Expected: a buyback bounded 300 bps from the market moves at most ~296 ticks; actual: 1068 ticks. (3b) Attacker contract in one transaction: router.swap(zeroForOne, exactInput 10,000,000e18 IMD, limit getSqrtPriceAtTick(1100)); hook.executeBatch(); sell every token received with limit MAX_SQRT_PRICE-1. Attacker IMD: 1,000,000e18 before, 1,024,133.23e18 after (profit 24,133e18); with a front-run to tick 1900 the profit is 2,880e18. Control without the drift (step 1 omitted, front-run to tick -100): attacker loses 1,240e18. Run: forge test --match-path test/scratch/StaleReference.t.sol -vv -> 3 FAIL (batchChasesStaleReference: 1068 > 305; sandwichProfit; sandwichSmallFrontRun), control passes.","severity":"medium","snippet":"        uint160 limitX96 = _priceLimit(referenceX96);","title":"executeBatch bounds its price limit only against the epoch TWAP, so after intra-epoch drift the batch moves the pool far more than 300 bps from spot and a same-block sandwich of the buyback is net pro"},{"citation":"resolved","description":"executeBatch() writes lastBatch = block.timestamp before it knows whether anything will be bought, and restarts the TWAP epoch afterwards regardless of what was spent. A filled batch and an empty attempt are treated identically. Because the caller also controls the pool price at the moment of the call, an unprivileged actor can make `room` false on purpose inside one transaction: buy enough launch token to move spot more than 300 bps (in price) past the epoch TWAP, call executeBatch() (returns (0,0), slot consumed), and sell back. They never hold the position; their only cost is the 2.25% round-trip fees plus impact on the pump volume. Every honest keeper is then refused with BatchTooSoon for 3600 s, repeatable hourly. The same path is hit for free whenever the market moves >3% within an hour: a bot calls executeBatch() while spot is outside the band and the keeper's later call reverts. No IMD is lost (pending() unchanged) and the batch resumes an hour later, so this is a delay/griefing defect; the README states that out-of-limit attempts advance the epoch by design, but the hourly slot is consumed as well, at a cost to the griefer that is 1.4%-6% of the buyback blocked. Merged from audit_permissions and audit_economics (same root cause). Minimal fix preserving the brief: on a zero-fill attempt do not advance lastBatch (keeping the epoch reset so a stale reference still adapts), or revert with a dedicated error so an empty attempt has no state effect.","line":204,"path":"src/SIMDTESTHook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {SIMDTEST} from \"src/SIMDTEST.sol\";\nimport {SIMDTESTHook} from \"src/SIMDTESTHook.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {PoolManager} from \"v4-core/src/PoolManager.sol\";\nimport {IPoolManager} from \"v4-core/src/interfaces/IPoolManager.sol\";\nimport {IUnlockCallback} from \"v4-core/src/interfaces/callback/IUnlockCallback.sol\";\nimport {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 {StateLibrary} from \"v4-core/src/libraries/StateLibrary.sol\";\n\ncontract ScratchIMD2 is ERC20 {\n    constructor() ERC20(\"IMD\", \"IMD\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\ncontract ScratchRouter2 is IUnlockCallback {\n    IPoolManager immutable manager;\n\n    constructor(IPoolManager m) {\n        manager = m;\n    }\n\n    function swap(PoolKey memory key, SwapParams memory p) external returns (BalanceDelta) {\n        return abi.decode(manager.unlock(abi.encode(true, msg.sender, key, abi.encode(p))), (BalanceDelta));\n    }\n\n    function addLiquidity(PoolKey memory key, ModifyLiquidityParams memory p) external {\n        manager.unlock(abi.encode(false, msg.sender, key, abi.encode(p)));\n    }\n\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        require(msg.sender == address(manager));\n        (bool isSwap, address payer, PoolKey memory key, bytes memory payload) =\n            abi.decode(data, (bool, address, PoolKey, bytes));\n        BalanceDelta d;\n        if (isSwap) d = manager.swap(key, abi.decode(payload, (SwapParams)), \"\");\n        else (d,) = manager.modifyLiquidity(key, abi.decode(payload, (ModifyLiquidityParams)), \"\");\n        _settle(key.currency0, d.amount0(), payer);\n        _settle(key.currency1, d.amount1(), payer);\n        return abi.encode(d);\n    }\n\n    function _settle(Currency c, int128 amt, address payer) private {\n        if (amt < 0) {\n            manager.sync(c);\n            IERC20(Currency.unwrap(c)).transferFrom(payer, address(manager), uint256(-int256(amt)));\n            manager.settle();\n        } else if (amt > 0) {\n            manager.take(c, payer, uint128(amt));\n        }\n    }\n}\n\n/// @dev Griefer that pushes spot past the band, calls executeBatch for a zero fill, and sells back,\n///      all in one transaction, so it never holds the position.\ncontract SlotGriefer {\n    function run(SIMDTESTHook hook, ScratchRouter2 router, PoolKey memory key, bool buyDir, uint256 pump)\n        external\n        returns (uint256 spent, uint256 burned)\n    {\n        address imd = hook.IMD();\n        IERC20(imd).approve(address(router), type(uint256).max);\n        IERC20(hook.token()).approve(address(router), type(uint256).max);\n        router.swap(\n            key,\n            SwapParams(buyDir, -int256(pump), buyDir ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1)\n        );\n        (spent, burned) = hook.executeBatch();\n        uint256 held = IERC20(hook.token()).balanceOf(address(this));\n        router.swap(\n            key,\n            SwapParams(!buyDir, -int256(held), !buyDir ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1)\n        );\n    }\n}\n\ncontract BatchSlotGriefTest is Test {\n    using StateLibrary for IPoolManager;\n\n    address constant IMD = 0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7;\n    uint160 constant Q96 = 79228162514264337593543950336;\n\n    IPoolManager manager;\n    SIMDTEST token;\n    SIMDTESTHook hook;\n    ScratchRouter2 router;\n    PoolKey key;\n    bool buyDir;\n\n    function setUp() public {\n        manager = IPoolManager(address(new PoolManager(address(this))));\n        vm.etch(IMD, address(new ScratchIMD2()).code);\n        ScratchIMD2(IMD).mint(address(this), 100_000_000 ether);\n        token = new SIMDTEST();\n        bytes32 initHash =\n            keccak256(abi.encodePacked(type(SIMDTESTHook).creationCode, abi.encode(manager, address(token))));\n        bytes32 salt;\n        for (uint256 i;; ++i) {\n            address p = address(uint160(uint256(keccak256(abi.encodePacked(hex\"ff\", address(this), bytes32(i), initHash)))));\n            if (uint160(p) & 0x3fff == 0x20c4) {\n                salt = bytes32(i);\n                break;\n            }\n        }\n        hook = new SIMDTESTHook{salt: salt}(manager, address(token));\n        key = hook.poolKey();\n        buyDir = hook.pairedIsCurrency0();\n        router = new ScratchRouter2(manager);\n        token.approve(address(router), type(uint256).max);\n        IERC20(IMD).approve(address(router), type(uint256).max);\n        manager.initialize(key, Q96);\n        router.addLiquidity(key, ModifyLiquidityParams(-887220, 887220, 1_000_000 ether, 0));\n    }\n\n    function test_ZeroFillAttemptConsumesTheHourlySlot() public {\n        IERC20(IMD).transfer(address(hook), 400_000 ether);\n        vm.warp(block.timestamp + 3600);\n\n        // What an honest keeper's batch would do right now.\n        uint256 snap = vm.snapshotState();\n        (uint256 honestSpent, uint256 honestBurned) = hook.executeBatch();\n        vm.revertToState(snap);\n\n        SlotGriefer g = new SlotGriefer();\n        uint256 pump = 20_000 ether; // enough to push spot more than 3% in price past the hour TWAP\n        IERC20(IMD).transfer(address(g), pump);\n        (uint256 spent, uint256 burned) = g.run(hook, router, key, buyDir, pump);\n        uint256 cost = pump - IERC20(IMD).balanceOf(address(g));\n\n        emit log_named_decimal_uint(\"honest batch would spend (IMD)\", honestSpent, 18);\n        emit log_named_decimal_uint(\"honest batch would burn (SIMDTEST)\", honestBurned, 18);\n        emit log_named_decimal_uint(\"griefed batch spent (IMD)\", spent, 18);\n        emit log_named_decimal_uint(\"griefed batch burned (SIMDTEST)\", burned, 18);\n        emit log_named_decimal_uint(\"griefer cost (IMD)\", cost, 18);\n        assertEq(hook.lastBatch(), block.timestamp);\n\n        assertEq(spent + burned, 0, \"griefer's attempt filled\");\n\n        // Expected: with spot back inside the band, an honest keeper can still run this hour's batch.\n        // Actual: BatchTooSoon until lastBatch + 3600, although nothing was bought.\n        (bool ok, bytes memory ret) = address(hook).call(abi.encodeCall(hook.executeBatch, ()));\n        assertTrue(ok, \"zero-fill attempt consumed the hourly batch slot (BatchTooSoon)\");\n        (uint256 keeperSpent,) = abi.decode(ret, (uint256, uint256));\n        assertGt(keeperSpent, 0, \"honest keeper batch spent nothing\");\n    }\n}","reproduction":"State: pool at sqrtPrice 2^96 with full-range liquidity 1,000,000e18 units, 400,000e18 IMD transferred to the hook (budget 100,000e18), block.timestamp = initialization + 3600. Honest keeper calling executeBatch() now would spend 15,077.63 IMD and burn 14,670.72 SIMDTEST (snapshot/revert). Griefer contract G holding 20,000e18 IMD, in ONE transaction: (1) router swap buy exact-input 20,000e18 IMD with the loose limit -> spot more than 3% in price past the TWAP (tick 0); (2) G calls hook.executeBatch() -> returns (0,0), hook.lastBatch() == block.timestamp; (3) G sells all SIMDTEST back. Griefer cost: 876.70e18 IMD. Then hook.executeBatch() from anyone reverts BatchTooSoon until +3600 s although nothing was bought and spot is back inside the band. Second fixture (audit_economics): tick 0, 2,000,000e18 pending, front-run buy of 158,176e18 IMD to tick -310, executeBatch() returns (0,0), sell back; griefer cost 6,947e18 IMD, a 500,000e18 buyback postponed an hour. Run: forge test --match-path test/scratch/Proof_9aaca66e198a.t.sol -vv -> FAIL 'zero-fill attempt consumed the hourly batch slot (BatchTooSoon)' (verified on this tree).","severity":"low","snippet":"        lastBatch = block.timestamp;","title":"A zero-fill batch attempt consumes the hourly slot and resets the epoch, so anyone can lock the buyback out for an hour by pushing spot past the band in the same transaction"},{"citation":"resolved","description":"beforeInitialize() checks that the caller is the PoolManager and that the key is the hook's own pool, but not the `sender` argument. Initialization before the hook has code is impossible (Hooks.callHook reverts InvalidHookResponse on an empty return), so the window is only between hook deployment and the factory's PoolManager.initialize. The brief and README state the factory deploys and initializes in one transaction, which closes the window; the contract itself does not enforce it. If deployment were ever non-atomic, the first caller would set initialized = true and the opening tick (the first epoch's reference), and the factory's own initialize would revert with WrongPool wrapped in HookCallFailed. Kept at info because it is unreachable under the documented launch flow; the reference material names a `$factory` constructor argument for hooks that want to require sender == factory. Reported by audit_economics.","line":109,"path":"src/SIMDTESTHook.sol","reproduction":"State: SIMDTEST and SIMDTESTHook deployed, factory initialize not yet sent. (1) Any EOA calls manager.initialize(hook.poolKey(), TickMath.getSqrtPriceAtTick(50000)): succeeds; hook.initialized() == true; hook.referencePrice() == getSqrtPriceAtTick(50000). (2) Factory calls manager.initialize(hook.poolKey(), 79228162514264337593543950336): reverts WrappedError(hook, beforeInitialize.selector, WrongPool(), HookCallFailed()). The existing test test_RejectsOtherPoolsAndReinitialization shows the second initialize reverting; the first call succeeding from any address follows from the absence of any sender check on line 109.","severity":"info","snippet":"        if (initialized || PoolId.unwrap(key.toId()) != PoolId.unwrap(poolId)) {","title":"beforeInitialize accepts initialization from any sender; only an atomic deploy-and-initialize keeps a third party from opening the pool at an arbitrary price"},{"citation":"resolved","description":"After the batch swap, executeBatch() opens the next epoch at the tick the hook's own fill left behind (the limit tick when price-limited). With no external trades during the next hour, referencePrice() equals that limit price, so the next limit is another 300 bps beyond it: with pending() large relative to depth the hook can walk the token price up ~3% per hour on its own flow, and holders can sell into each successively higher bid. This is a consequence of the chosen epoch definition (the README documents it) rather than a coding error, and the alternative (excluding the hook's own fill) would leave a quiet pool with no room for any later batch. Noted for the author to confirm the trade-off. Reported by audit_flow.","line":211,"path":"src/SIMDTESTHook.sol","reproduction":"Token = currency0, pool at tick 0 with liquidity 10,000,000e18 in [-600, 600], 50,000,000e18 IMD held by the hook, no external swaps. Hour 1: executeBatch() with reference 2^96 spends 150,776.27e18 (price-limited), post-batch sqrtPriceX96 = 80407803025877290634728829553 (+2.99%). Hour 2: referencePrice() == 80405379686066038741618617935 (hour-1 limit price floored to a tick), spends 152,706.85e18, +6.07% vs launch. Hour 3: +9.25% (end of the seeded range). Follows from line 211 setting observedTick to the post-batch slot0.tick with cumulativeTick = 0.","severity":"info","snippet":"        (, observedTick,,) = poolManager.getSlot0(poolId);","title":"Each batch re-anchors the reference epoch at the post-batch tick, so in a quiet pool the band compounds on the hook's own price impact hour after hour"},{"citation":"resolved","description":"M1 says 'a sqrtPriceLimit 300 bps beyond a time-weighted reference price'. The implementation moves the sqrt price by sqrt(1.03)-1 = 1.489%, i.e. 300 bps in squared (pool) price, and the README documents this as deliberate. It is tighter than the literal reading, so it cannot cause overpayment; every price-limited batch fills roughly half of what a literal 300 bps sqrt-price band would allow and the remainder waits for the next hour. Recorded so the requester can confirm which reading is intended. Reported by audit_flow.","line":262,"path":"src/SIMDTESTHook.sol","reproduction":"referencePrice() == 79228162514264337593543950336 (tick 0). Token = currency0 (hook buys oneForZero): _priceLimit returns 79228162514264337593543950336 * 1014889156509221946 / 1e18 = 80407803025877290634728829553; the literal reading would give 79228162514264337593543950336 * 103 / 100 = 81605007389692267721350268846. Token = currency1: mulDivRoundingUp(ref, 984885780179610473, 1e18) = 78030690650058201001108743414 vs ref * 97 / 100 = 76851317638836407465737631825. test_BatchPriceLimitAcceptsPartialFillAndRemainderLater asserts the implemented values.","severity":"info","snippet":"        uint256 limit = pairedIsCurrency0","title":"The 300 bps band is applied to pool price (sqrt factors 0.98489 / 1.01489), not to the sqrtPriceLimit itself as the brief's sentence literally reads"},{"citation":"resolved","description":"fee = floor(|unspecified delta| / 100). Fees conventionally round up; here each fill pays at most 1% and dust fills pay 0. With 18-decimal currencies the shortfall is at most 99 wei per swap and gas makes splitting a trade into sub-100-wei pieces absurd, so there is no economic effect; the README documents the floor. No action required. Reported by audit_math.","line":149,"path":"src/SIMDTESTHook.sol","reproduction":"Seeded pool (liquidity [-600,600], 1e25). Exact-output buy of 199 wei token: gross delta (token +199, IMD -205), fee = 205/100 = 2 wei (0.976%). Exact-input sell whose IMD output is 99 wei: fee 0. test_DustSwapsAroundOnePercentRoundingBoundary shows amounts 1..99 pay 0 fee.","severity":"info","snippet":"        uint256 amount = uint256(filled < 0 ? -filled : filled) / 100;","title":"Hook fee rounds down, so fills below 100 wei pay nothing and small fills pay slightly less than 1%"},{"citation":"resolved","description":"referencePrice() returns getSqrtPriceAtTick(floor(meanTick)), lowering the reference by up to one tick (0.01%). After a zeroForOne swap that stops exactly on an initialized tick, core stores slot0.tick = tickNext - 1 although the price equals getSqrtPriceAtTick(tickNext), so an observation can be one tick below the price tick. When IMD is currency0 the batch's limit is reference * sqrt(0.97) (rounded up by at most 1 wei of sqrt price), so the pool price the hook may push to is up to 0.97 * 1.0001^-2 of the true geometric mean: about 302 bps instead of 300. When the token is currency0 both effects tighten the band. Negligible against the 300 bps tolerance; noted so the asymmetry is a known choice. Reported by audit_math.","line":182,"path":"src/SIMDTESTHook.sol","reproduction":"IMD < token (pairedIsCurrency0 == true), epoch of 3600 s with tick 0 for 1800 s and tick -1 for 1800 s: sum = -1800, mean = -0.5, floored to -1; referencePrice() = getSqrtPriceAtTick(-1) = 79224201403219477170569942573 instead of the price at -0.5. limitX96 = mulDivRoundingUp(ref, 984885780179610473, 1e18); squared ratio to the true mean price = 0.97 * 1.0001^-1 = 0.969903 (300.97 bps); with the tickNext-1 convention adding another tick, 0.969806 (301.9 bps).","severity":"info","snippet":"        return TickMath.getSqrtPriceAtTick(int24(mean));","title":"Reference tick is floored and slot0.tick can sit one below the price tick, so the band is up to ~2 bps wider when IMD is currency0"}],"hash":"5f48a916e6f66cc2b11bbcb3d6376a0bc108ed5547251fd21f0a318d02d7dc2d","nodeId":"a641cabc-fe76-45cb-aaa2-4ad320d771be","outcome":"completed","summary":"The review is complete. The findings are in `.imd-findings.json` at the repository root, with 8 findings and a 12-row coverage record. No tracked file was changed; the only additions are the findings file and my scratch tests under the gitignored `test/scratch/`.\n\n**What I verified before judging.** The project builds and its full suite passes (68 tests, 2 fork skips). I ran both specialist proofs against this tree and they fail for the stated reasons. For the one medium claim that came without a proof, I wrote an independent test that reproduces the specialist's numbers exactly.\n\n**Findings kept, after merging and recalibration**\n\n1. **Medium, zero-fill void tick poisons the reference.** A 1-wei sell toward the pool's empty side moves the tick to the global minimum for gas only, and the hook time-weights that untraded tick into the batch reference. The next batch spends nothing and consumes the hour. Merged from audit_flow and audit_math. Proof attached.\n2. **Medium, stale TWAP lets the batch chase a lagging reference.** After a 3% intra-epoch drift the batch moved the pool over 10% from spot, and a same-block sandwich netted the attacker 4.8% of the batch, paid from accrued IMD. The no-drift control loses money, so the lag is the defect, not the band. The implementation follows the brief literally, which I state in the finding. Proof attached.\n3. **Low, zero-fill attempt consumes the hourly slot.** A griefer can pump past the band, call the batch, and sell back in one transaction, blocking honest keepers for an hour at a cost of about 1.4% to 6% of the blocked buyback. Merged from audit_permissions and audit_economics. Proof attached.\n4. **Info, four items.** Initialization accepts any sender (unreachable under atomic deploy), epoch re-anchoring on the hook's own fill, the 300 bps band applied to pool price rather than sqrt price, fee rounding down, and a 2 bps band asymmetry from tick flooring. All reproduce but none is a defect with impact.\n\n**My own pass found nothing further.** Fee delta accounting nets to zero on every path, callbacks and the unlock callback are properly guarded, settlement burns only the claims actually spent, the manifest matches the hook's permissions and constructor, and there is no owner, proxy, selfdestruct, delegatecall or dynamic fee. The entry points for token transfers, sweep and unlockCallback are marked as holding; afterSwap, beforeInitialize and executeBatch point to findings 1, 4 and 2.","treeHash":null,"usage":{"cachedInputTokens":1331143,"inputTokens":290,"model":"claude-fable-5-1","outputTokens":42356,"runtime":"claude","turns":42,"wallClockMs":639505}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"11e4593175677f7e","findings":[{"citation":"resolved","description":"executeBatch() derives its only slippage guard, limitX96, from referencePrice(): the time-weighted mean tick since the epoch started (initialization or the previous eligible attempt). The limit is 300 bps from that reference, but nothing relates it to the pool price at the moment the batch runs. Whenever the price moved during the epoch in the direction that makes the launched token cheaper (c1/c0 rises when IMD is currency0), the reference lags behind spot and the limit sits far from spot. The batch then swaps exact-input with a limit that lets it move the pool by the whole lag plus 300 bps, buying tokens well above the current market. Because executeBatch() is permissionless and the keeper chooses the timing, the keeper can front-run its own call with a buy at the depressed price, let the batch push the price up toward the stale reference, and sell back in the same block; the gain exceeds the 2.25% fee (1.25% LP + 1% hook) paid on each leg. Measured in the attached test with 10M liquidity units in [-12000, 12000] and 2,000,000 IMD pending (budget 500,000): the market sells the token to tick +2000 at 3000 s into the epoch; at 3600 s the reference is tick 333 and the limit is about tick 28; the batch spends 502,378 IMD, moves the pool price 1012 bps (tick 2000 to 932) and burns 574,436 tokens where 613,600 would have been bought at the pre-batch spot (638 bps overpayment, about 39k tokens of value). A front-run buy to tick 1900 (45,927 IMD at risk) nets the sandwicher 2,880 IMD after fees; a larger front-run to tick 1100 (421,729 IMD at risk) nets 24,133 IMD, 4.8% of the batch. The IMD comes from the hook's accrued fees, so holders lose burned supply to the sandwicher and to LP fees. Both tests in the proof pass once the limit is also bounded by 300 bps from the pre-batch spot (take the tighter of the two limits: with that change the batch moves the pool 299 bps and the sandwicher loses 673 IMD). This keeps the TWAP as the anti-manipulation anchor while preventing the batch from chasing a stale reference.","line":200,"path":"src/SIMDTESTHook.sol","reproduction":"State: pool initialized at tick 0 (IMD = currency0, token = currency1), liquidity 10M units over ticks [-12000, 12000], hook holds 2,000,000 IMD (pending). Sequence: (1) warp +3000 s; a market seller swaps oneForZero exact-input with sqrtPriceLimit = getSqrtPriceAtTick(2000), leaving spot at tick 2000. (2) warp +600 s (epoch length 3600). referencePrice() = tick 333, limit = tick 28. (3) Attacker: router.swap(zeroForOne, exactInput 10,000,000 IMD, limit getSqrtPriceAtTick(1100)) buys 421,729 IMD worth of tokens; then hook.executeBatch() spends 502,378 IMD moving the price to near tick 1100-305; then attacker sells the tokens bought with no limit. Expected: the batch should not execute more than 300 bps away from the current market, and a same-block sandwich should not be net profitable after 4.5% round-trip fees. Actual: pool price moved 1012 bps in the unsandwiched case; attacker IMD balance rises by 24,133 (2,880 with a tick-1900 front-run). Run: forge test --match-path test/scratch/StaleReference.t.sol -vv.","severity":"medium","snippet":"        uint160 limitX96 = _priceLimit(referenceX96);","title":"executeBatch bounds its price limit only against the epoch TWAP, so after intra-epoch drift the buyback chases a stale reference, moves the pool far beyond 300 bps, and can be sandwiched for profit"},{"citation":"resolved","description":"executeBatch() writes lastBatch = block.timestamp before checking whether the spot is inside the limit, and resets the epoch afterwards regardless of what was spent. The README documents this as intentional so the reference can adapt, but it also means any party who sees a keeper's executeBatch() in the mempool can make it spend nothing: a buy that pushes the spot just past the limit (300 bps from the reference) is enough. The attempt then returns (0, 0), lastBatch moves forward, BatchTooSoon blocks every retry for 3600 s, and the front-runner unwinds in the same block (the manipulated tick gets zero weight because elapsed is 0). Measured with the proof fixture of finding 1 in a flat market (reference tick 0, limit tick -305): a front-run buy of 158,176 IMD to tick -310, executeBatch() returning (0, 0) with lastBatch updated, and a same-block sell-back cost the griefer 6,947 IMD in fees plus impact, while a 500,000 IMD buyback was postponed by an hour. Repeating it every hour costs about 1.4% of the budget per hour blocked. A short seller or competitor has a motive; no capital is lost by the hook, so this is a liveness issue. A mitigation is to leave lastBatch untouched when nothing was spent (keep the epoch reset so the reference still adapts), or to make a zero-fill attempt wait a shorter interval.","line":204,"path":"src/SIMDTESTHook.sol","reproduction":"State: same fixture as finding 1 (tick 0, 2,000,000 IMD pending), warp +3600 so an attempt is eligible and referencePrice() == Q96. Sequence: (1) attacker swaps zeroForOne exact-input 10,000,000 IMD with limit getSqrtPriceAtTick(-310) (fills 158,176 IMD). (2) anyone calls hook.executeBatch(): returns (0, 0), hook.lastBatch() == block.timestamp. (3) attacker sells the tokens received with no limit; net cost 6,947 IMD. (4) hook.executeBatch() reverts BatchTooSoon until +3600 s; pending() is unchanged at 2,000,000. Expected: a failed or empty attempt should not consume the keeper's hourly slot. Actual: the slot and the epoch are consumed by the zero-fill attempt.","severity":"low","snippet":"        lastBatch = block.timestamp;","title":"A zero-fill batch attempt consumes the hourly slot and resets the epoch, so a front-runner can block each 500k IMD buyback for about 6.9k IMD"},{"citation":"resolved","description":"beforeInitialize() checks only that the caller is the PoolManager and that the key is the hook's own pool. It does not check the initializer (the sender argument). If the launch factory deploys the hook in one transaction and calls PoolManager.initialize in a later one, anyone can call PoolManager.initialize(hook.poolKey(), anyPrice) in between: the hook sets initialized = true, observedTick to the attacker's tick (this becomes the first epoch reference and the first batch limit), and the factory's subsequent initialize reverts with WrongPool through Hooks.HookCallFailed, so the launch cannot open on this hook address. The README states that deployment and initialization must be atomic, and the brief describes the factory doing both in one transaction, in which case this is unreachable; the finding is recorded because the contract itself does not enforce it. The reference material for IMD launches names the factory with a constructor argument written \"$factory\"; requiring sender == factory in beforeInitialize closes the window without changing anything else.","line":109,"path":"src/SIMDTESTHook.sol","reproduction":"State: SIMDTEST and SIMDTESTHook deployed (hook not yet initialized), factory initialize not yet sent. Sequence: (1) attacker calls manager.initialize(hook.poolKey(), TickMath.getSqrtPriceAtTick(50000)) from any EOA: succeeds, hook.initialized() == true, hook.referencePrice() == getSqrtPriceAtTick(50000). (2) factory calls manager.initialize(hook.poolKey(), 79228162514264337593543950336): reverts with WrappedError(hook, beforeInitialize.selector, WrongPool(), HookCallFailed()) (the existing test test_RejectsOtherPoolsAndReinitialization shows the second initialize reverting). Expected: only the launch factory can open the pool. Actual: first caller wins and the factory's launch transaction fails.","severity":"low","snippet":"        if (initialized || PoolId.unwrap(key.toId()) != PoolId.unwrap(poolId)) {","title":"beforeInitialize accepts initialization from any sender, so a non-atomic deploy lets a third party open the pool at an arbitrary price and brick the factory's own initialize"}],"hash":"6df6b7c717ad76e3d2b1500f3cf5e2859aac4b4cde6c2dda6537c50268c95939","nodeId":"a6f99ac8-df6b-49c9-98c3-fafe98e8b1ce","outcome":"completed","summary":"The review is complete. The findings file is written at `.imd-findings.json`, all three snippets match the tree, the scratch directory is removed, the source is restored byte-for-byte, and `git status` shows no tracked changes.\n\n**Findings**\n\n- **Medium, `src/SIMDTESTHook.sol:200`.** The batch's only slippage guard is 300 bps from the epoch TWAP, with no relation to the current spot. After the token sold off late in an epoch, the batch chased the stale reference: it moved the pool 1012 bps, burned 574k tokens where 614k were available at spot, and a same-block keeper sandwich netted 2,880 to 24,133 IMD after the 4.5% round-trip fees. The embedded proof fails now and passes when the limit is also bounded to 300 bps from the pre-batch spot.\n- **Low, `src/SIMDTESTHook.sol:204`.** A zero-fill attempt still consumes the hourly slot, so a front-runner who pushes spot past the limit blocks a 500k IMD buyback for about 6.9k IMD per hour.\n- **Low, `src/SIMDTESTHook.sol:109`.** Any sender may initialize the pool. This only matters if the factory's deploy and initialize are not in one transaction, in which case a third party can open the pool at any price and the factory's initialize reverts.\n\n**Coverage.** All eight entry points have rows: six hold, two carry findings. Fee proportionality, claim conservation, the no-fee self-swap, and the no-revert swap domain were traced and hold.\n\n**Verification beyond the repo's suite.** I ran the repository's mainnet fork suite against a public RPC, which the README lists as outstanding. All 56 tests passed against the real PoolManager and the real IMD contract, which is the Identity.md LayerZero OFT with 18 decimals and no pause or blacklist selectors. The sqrt price-limit constants were also confirmed correct.","treeHash":null,"usage":{"cachedInputTokens":2912411,"inputTokens":642,"model":"claude-fable-5-1","outputTokens":62377,"runtime":"claude","turns":46,"wallClockMs":917124}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"a4c81f495eb81dd0","findings":[{"citation":"resolved","description":"Trust-gap seam (access x economics x asymmetry). executeBatch() is callable by anyone and writes lastBatch = block.timestamp before it knows whether anything will be bought. When `budget != 0 && room` is false the function skips the swap but still (a) consumes the 3600-second slot and (b) restarts the TWAP epoch at the current tick. A filled batch and an empty attempt are therefore treated identically, although only one of them bought anything. Because the caller also controls the pool price at the moment of the call, an unprivileged actor can make `room` false on purpose: in one transaction they buy enough launch token to move spot more than 300 bps (in price) past the hour's TWAP, call executeBatch() (zero fill, slot consumed), and sell back. They never hold the position, so their only cost is the 2.25% round-trip fees on the pump volume. Every honest keeper is then refused with BatchTooSoon for the next hour, and the manoeuvre can be repeated each hour. No IMD is lost (pending() is unchanged), so this is a delay/griefing defect rather than a loss of funds, and the README states that out-of-limit attempts advance the epoch by design; the defect is that the hourly slot is consumed as well, with no fill, at a cost to the griefer that is a small fraction of the buyback it blocks. The same path is hit for free whenever the market moves >3% within an hour: a bot can call executeBatch() the moment spot is outside the band and the keeper's later call reverts. Minimal fix preserving the brief: on a zero-fill attempt do not advance lastBatch (keep the slot available) and do not restart the epoch (otherwise a free zero-fill call would let anyone shorten the TWAP window at will); alternatively require `budget != 0 && room` and revert with a dedicated error so the attempt has no state effect. Either choice is a design decision for the author because the README deliberately restarts the epoch to let a stale reference adapt.","line":204,"path":"src/SIMDTESTHook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {SIMDTEST} from \"src/SIMDTEST.sol\";\nimport {SIMDTESTHook} from \"src/SIMDTESTHook.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {PoolManager} from \"v4-core/src/PoolManager.sol\";\nimport {IPoolManager} from \"v4-core/src/interfaces/IPoolManager.sol\";\nimport {IUnlockCallback} from \"v4-core/src/interfaces/callback/IUnlockCallback.sol\";\nimport {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 {StateLibrary} from \"v4-core/src/libraries/StateLibrary.sol\";\n\ncontract ScratchIMD2 is ERC20 {\n    constructor() ERC20(\"IMD\", \"IMD\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\ncontract ScratchRouter2 is IUnlockCallback {\n    IPoolManager immutable manager;\n\n    constructor(IPoolManager m) {\n        manager = m;\n    }\n\n    function swap(PoolKey memory key, SwapParams memory p) external returns (BalanceDelta) {\n        return abi.decode(manager.unlock(abi.encode(true, msg.sender, key, abi.encode(p))), (BalanceDelta));\n    }\n\n    function addLiquidity(PoolKey memory key, ModifyLiquidityParams memory p) external {\n        manager.unlock(abi.encode(false, msg.sender, key, abi.encode(p)));\n    }\n\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        require(msg.sender == address(manager));\n        (bool isSwap, address payer, PoolKey memory key, bytes memory payload) =\n            abi.decode(data, (bool, address, PoolKey, bytes));\n        BalanceDelta d;\n        if (isSwap) d = manager.swap(key, abi.decode(payload, (SwapParams)), \"\");\n        else (d,) = manager.modifyLiquidity(key, abi.decode(payload, (ModifyLiquidityParams)), \"\");\n        _settle(key.currency0, d.amount0(), payer);\n        _settle(key.currency1, d.amount1(), payer);\n        return abi.encode(d);\n    }\n\n    function _settle(Currency c, int128 amt, address payer) private {\n        if (amt < 0) {\n            manager.sync(c);\n            IERC20(Currency.unwrap(c)).transferFrom(payer, address(manager), uint256(-int256(amt)));\n            manager.settle();\n        } else if (amt > 0) {\n            manager.take(c, payer, uint128(amt));\n        }\n    }\n}\n\n/// @dev Griefer that pushes spot past the band, calls executeBatch for a zero fill, and sells back,\n///      all in one transaction, so it never holds the position.\ncontract SlotGriefer {\n    function run(SIMDTESTHook hook, ScratchRouter2 router, PoolKey memory key, bool buyDir, uint256 pump)\n        external\n        returns (uint256 spent, uint256 burned)\n    {\n        address imd = hook.IMD();\n        IERC20(imd).approve(address(router), type(uint256).max);\n        IERC20(hook.token()).approve(address(router), type(uint256).max);\n        router.swap(\n            key,\n            SwapParams(buyDir, -int256(pump), buyDir ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1)\n        );\n        (spent, burned) = hook.executeBatch();\n        uint256 held = IERC20(hook.token()).balanceOf(address(this));\n        router.swap(\n            key,\n            SwapParams(!buyDir, -int256(held), !buyDir ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1)\n        );\n    }\n}\n\ncontract BatchSlotGriefTest is Test {\n    using StateLibrary for IPoolManager;\n\n    address constant IMD = 0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7;\n    uint160 constant Q96 = 79228162514264337593543950336;\n\n    IPoolManager manager;\n    SIMDTEST token;\n    SIMDTESTHook hook;\n    ScratchRouter2 router;\n    PoolKey key;\n    bool buyDir;\n\n    function setUp() public {\n        manager = IPoolManager(address(new PoolManager(address(this))));\n        vm.etch(IMD, address(new ScratchIMD2()).code);\n        ScratchIMD2(IMD).mint(address(this), 100_000_000 ether);\n        token = new SIMDTEST();\n        bytes32 initHash =\n            keccak256(abi.encodePacked(type(SIMDTESTHook).creationCode, abi.encode(manager, address(token))));\n        bytes32 salt;\n        for (uint256 i;; ++i) {\n            address p = address(uint160(uint256(keccak256(abi.encodePacked(hex\"ff\", address(this), bytes32(i), initHash)))));\n            if (uint160(p) & 0x3fff == 0x20c4) {\n                salt = bytes32(i);\n                break;\n            }\n        }\n        hook = new SIMDTESTHook{salt: salt}(manager, address(token));\n        key = hook.poolKey();\n        buyDir = hook.pairedIsCurrency0();\n        router = new ScratchRouter2(manager);\n        token.approve(address(router), type(uint256).max);\n        IERC20(IMD).approve(address(router), type(uint256).max);\n        manager.initialize(key, Q96);\n        router.addLiquidity(key, ModifyLiquidityParams(-887220, 887220, 1_000_000 ether, 0));\n    }\n\n    function test_ZeroFillAttemptConsumesTheHourlySlot() public {\n        IERC20(IMD).transfer(address(hook), 400_000 ether);\n        vm.warp(block.timestamp + 3600);\n\n        // What an honest keeper's batch would do right now.\n        uint256 snap = vm.snapshotState();\n        (uint256 honestSpent, uint256 honestBurned) = hook.executeBatch();\n        vm.revertToState(snap);\n\n        SlotGriefer g = new SlotGriefer();\n        uint256 pump = 20_000 ether; // enough to push spot more than 3% in price past the hour TWAP\n        IERC20(IMD).transfer(address(g), pump);\n        (uint256 spent, uint256 burned) = g.run(hook, router, key, buyDir, pump);\n        uint256 cost = pump - IERC20(IMD).balanceOf(address(g));\n\n        emit log_named_decimal_uint(\"honest batch would spend (IMD)\", honestSpent, 18);\n        emit log_named_decimal_uint(\"honest batch would burn (SIMDTEST)\", honestBurned, 18);\n        emit log_named_decimal_uint(\"griefed batch spent (IMD)\", spent, 18);\n        emit log_named_decimal_uint(\"griefed batch burned (SIMDTEST)\", burned, 18);\n        emit log_named_decimal_uint(\"griefer cost (IMD)\", cost, 18);\n        assertEq(hook.lastBatch(), block.timestamp);\n\n        assertEq(spent + burned, 0, \"griefer's attempt filled\");\n\n        // Expected: with spot back inside the band, an honest keeper can still run this hour's batch.\n        // Actual: BatchTooSoon until lastBatch + 3600, although nothing was bought.\n        (bool ok, bytes memory ret) = address(hook).call(abi.encodeCall(hook.executeBatch, ()));\n        assertTrue(ok, \"zero-fill attempt consumed the hourly batch slot (BatchTooSoon)\");\n        (uint256 keeperSpent,) = abi.decode(ret, (uint256, uint256));\n        assertGt(keeperSpent, 0, \"honest keeper batch spent nothing\");\n    }\n}","reproduction":"State: pool initialized at sqrtPrice 2^96 with full-range liquidity of 1,000,000 units (about 1M IMD and 1M SIMDTEST), 400,000 IMD transferred to the hook (pending() = 400,000e18, budget = 100,000e18), block.timestamp = initialization + 3600 so a batch is eligible. Honest keeper calling executeBatch() now would spend 15,077.6 IMD and burn 14,670.7 SIMDTEST (price-limited to the 3% band). Instead, griefer contract G with 20,000 IMD does in ONE transaction: (1) router swap buy exact-input 20,000 IMD -> spot moves >3% past the TWAP (reference = tick 0); (2) G calls hook.executeBatch() -> room == false, returns (0, 0), but lastBatch = block.timestamp and epoch restarted; (3) G sells all SIMDTEST back. Griefer cost: 876.7 IMD (fees). Immediately after, in the same block or any time before +3600s, honest keeper's hook.executeBatch() reverts BatchTooSoon although nothing was bought and spot is back inside the band. Expected: an attempt that bought nothing should not lock the hour; actual: 15,077 IMD of buyback delayed by one hour for a 877 IMD griefing cost, repeatable hourly. Run: forge test --match-path test/scratch/BatchSlotGrief.t.sol -vv (fails on current code with 'zero-fill attempt consumed the hourly batch slot (BatchTooSoon)').","severity":"low","snippet":"        lastBatch = block.timestamp;\n        (uint160 spot,,,) = poolManager.getSlot0(poolId);\n        bool room = pairedIsCurrency0 ? spot > limitX96 : spot < limitX96;\n        if (budget != 0 && room) {\n            (spent, burned) = abi.decode(poolManager.unlock(abi.encode(budget, limitX96)), (uint256, uint256));\n        }","title":"executeBatch consumes the hourly slot and resets the reference epoch on a zero-fill attempt, so anyone can lock the buyback out for an hour by pushing spot past the band in the same transaction"}],"hash":"75f3943de96e57347647beca1bb6a12586288477492ab5b5cb6d1fcd99acdd11","nodeId":"a5131e31-4d27-49d9-b480-86153e206b0c","outcome":"completed","summary":"The findings file exists and is valid; it is only hidden from `git status` by a global ignore rule, and no other new files remain in the tree. The review is complete.\n\n## Outcome\n\n**Audit area:** Access Control, Trust Gap and Asymmetry on the SIMDTEST launch (token plus hook). Result: one low-severity finding, written to `.imd-findings.json` with a self-contained proof test and coverage rows for all eight entry points plus three invariants.\n\n**Finding 1 (low): a zero-fill batch attempt still consumes the hourly slot.** `executeBatch()` writes `lastBatch` and restarts the TWAP epoch before it knows whether the swap will fill. Anyone can push spot past the 3% band, call the function for a zero fill, and sell back in the same transaction. Measured on a 1M-deep pool with a 100k IMD budget:\n\n| Item | Value |\n| --- | --- |\n| Honest batch that was blocked | 15,078 IMD spent, 14,671 SIMDTEST burned |\n| Griefer cost (round-trip fees) | 877 IMD |\n| Effect | Keepers get `BatchTooSoon` for the next hour, repeatable hourly |\n\nNo IMD is lost, only delayed, and the README deliberately restarts the epoch on out-of-band attempts, so I rated it low and described the design trade-off a fix must respect.\n\n**What holds in my area.** Every callback is PoolManager-only, `unlockCallback` only dispatches on hook-set state, the fee-exempt swap path is reachable only by the hook itself, no third party can move the hook's ERC-6909 claims, and the manifest's permissions and constructor arguments match the implementation. The sustained TWAP manipulation the Trust Gap guide warns about was tested and is unprofitable here: the 3% band bounds the attacker's gain below their 4.5% round-trip fee, and a 300k IMD pump lost 4,217 IMD to cut a single batch's burn by 24%.\n\n**Extra evidence gathered.** The README lists the mainnet fork rehearsal as outstanding. A public RPC was reachable, so I ran both fork suites against the live PoolManager and real IMD at block 26146380: all 56 tests pass. IMD's deployed code is a LayerZero OFT on a standard OpenZeppelin ERC-20 with no pause, blocklist or transfer fee, which matches the hook's assumptions.\n\n**Not covered.** Launch economics (opening price, seeded range, whether anyone is incentivised to call `executeBatch` at all) are outside the permission model and were not assessed beyond noting that no keeper reward exists.","treeHash":null,"usage":{"cachedInputTokens":2564146,"inputTokens":514,"model":"claude-fable-5-1","outputTokens":57991,"runtime":"claude","turns":40,"wallClockMs":1137060}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"dc34db8e17664ebd","findings":[{"citation":"resolved","description":"Introduced by the fix to the zero-fill slot-grief finding (a7e7d10d...). Before the fix every eligible attempt restarted the TWAP epoch, so referencePrice() never averaged more than the time since the last attempt; now epochStart moves only when a batch actually spends IMD. The author's reason for keeping history is sound (a free epoch reset would let an attacker pump, reset, and buy at the pumped tick), but the consequence is that the epoch has no upper bound. referencePrice() is the time-weighted mean tick since the last FILLED batch, and executeBatch() only has room when spot is within 300 bps (pool price) of that mean in the hook's buy direction. If the launched token's price rises by more than 300 bps and stays there, the batch returns (0,0) until the lifetime mean converges to within 305 ticks of spot, which takes about idle x (moveTicks - 305) / 305 seconds, where idle is the epoch length at the time of the move. With hourly filled batches (idle ~ 1 h) a 10% move stalls 3 hours and a 50% move 13 hours; but after any period without a filled batch (no keeper calls, no fees yet, or a price that sat out of band) the stall scales with that period: 1 day idle + 10% rally = 51-54 hours, 7 days = 357-375 hours (15 days), 30 days = 1530-1606 hours (65 days). The first fill after the stall is tiny (about 320e18 of a 250,000e18 budget, because the limit then sits just beyond spot), and only after that does the epoch reset and hourly cadence resume at full budget. No IMD is lost (pending() keeps accruing) and a fall in price unblocks immediately via the spot bound, so this is a liveness delay under a specific condition, not a loss; it is reported because the revised epoch semantics were not exercised beyond one-hour epochs by the new regressions, and the author should confirm the trade-off or bound the epoch (for example, weight only the most recent BATCH_INTERVAL, or cap elapsed in referencePrice() at a fixed window with the last observation carrying the remainder, which keeps an attempt from resetting the window for free).","line":219,"path":"src/SIMDTESTHook.sol","reproduction":"Local PoolManager, both currency orderings (token = currency0 and token = currency1), pool initialized at sqrtPrice 2^96 (tick 0), full-range-like liquidity 10,000,000e18 units over [-60000, 60000], 1,000,000e18 IMD transferred to the hook. (1) warp +idle seconds with no swaps and no batch. (2) One exact-input buy of 100,000,000e18 IMD with sqrtPriceLimit = getSqrtPriceAtTick(-953) when IMD is currency0, or getSqrtPriceAtTick(+953) when the token is currency0 (token price +10%); spot stays there. (3) Every 3600 s call executeBatch() and record the first call whose returned spent != 0. Results (token0 ordering / token1 ordering): idle 3600 s -> first fill at hour 3 / 3; idle 86400 s -> hour 51 / 54; idle 604800 s -> hour 357 / 375; idle 2592000 s -> hour 1530 / 1606. Same fixture with idle 3600 s and a 50% move (tick 4055): first fill at hour 13 / 13. First fill after a long stall spends 322490673711190894198 / 320099652825793523243 against a 250,000e18 budget. Expected: with 1,000,000e18 pending, ample liquidity at spot and a price that has been stable for hours, the buyback resumes within a bounded window (the pre-fix code resumed one hour after the first attempt). Actual: executeBatch() returns (0,0) for days to months, proportional to how long the previous epoch had already run. The stall ends only when the lifetime mean tick comes within 305 ticks of spot or the price falls back.","severity":"low","snippet":"        if (spent != 0) {\n            lastBatch = block.timestamp;\n            if (poolManager.getLiquidity(poolId) != 0) (, observedTick,,) = poolManager.getSlot0(poolId);\n            cumulativeTick = 0;\n            observedAt = block.timestamp;\n            epochStart = block.timestamp;\n        }","title":"Empty batch attempts no longer bound the reference epoch, so after an idle stretch the TWAP becomes a lifetime mean and a sustained rally stalls the buyback for multiples of the idle period"}],"hash":"77d266dce52cce2ad57934316cda1d83f3d52536cf2ae5fbaaab625358c75903","nodeId":"a641cabc-fe76-45cb-aaa2-4ad320d771be","outcome":"completed","summary":"The review is complete and written to `.imd-findings.json`. No source files were changed, and the scratch tests were removed.\n\n**Settlement of the earlier findings**\n\n- **Zero-fill void tick poisons the reference (medium): fixed.** Both attached proofs now pass on this tree. The batch spends the full 250e18 budget and burns about 245e18 tokens where it previously spent nothing. The hook now adopts a post-swap tick only when active liquidity is nonzero.\n- **Batch chases a stale TWAP and is sandwichable (medium): fixed.** The price limit is now the tighter of the TWAP band and a 300 bps band from liquid spot. The author's regressions in both orderings show the drifted batch moving price by exactly 3% and both sandwich sizes losing money, with 1000 fuzz runs per ordering.\n- **Zero-fill attempt consumes the hourly slot (low): fixed.** The attached proof fails only on an intermediate assertion that encoded the old state, where lastBatch was advanced. With that one line removed the proof passes: the same-block retry fills 15,077 IMD.\n- **Five info items: dropped.** The author's disputes hold. They are documented design choices, not defects.\n\n**One new finding, low severity, introduced by the grief fix**\n\nEmpty attempts no longer reset the epoch, so the reference is the mean tick since the last filled batch with no upper bound on that window. When the token rises more than 3% and holds, the batch returns zero until the lifetime mean converges to within 305 ticks of spot. Measured in both orderings with a 10% rise:\n\n| Prior epoch length | Hours until first fill |\n| --- | --- |\n| 1 hour | 3 |\n| 1 day | 51 to 54 |\n| 7 days | 357 to 375 |\n| 30 days | 1530 to 1606 |\n\nA 50% rise after a one-hour epoch stalls 13 hours. No IMD is lost and a price fall unblocks immediately, so this is a liveness delay for the author to accept or bound, for instance by weighting only a fixed recent window. The author's reason for keeping history across empty attempts is correct, since a free reset would let an attacker pump, reset, and buy at the pumped tick.\n\n**Coverage** answers all eight entry points. The full project suite passes with 102 tests and 2 fork tests skipped offline.","treeHash":null,"usage":{"cachedInputTokens":853820,"inputTokens":258,"model":"claude-fable-5-1","outputTokens":25002,"runtime":"claude","turns":22,"wallClockMs":425842}},{"artifacts":[],"attempt":1,"bundleHash":"74acc8e50fcdb0ba3a0a2f36778582eb11ada058bab2138d22afa9e94c1f4bc0","device":"7d4a49a940f788b7","findings":[],"hash":"a01c5c6b983eff34791034fbcfbeadcc8df90b47da160cca0e819b5b4ef5d9ff","nodeId":"1c1195cf-dc50-4b5a-a49e-b7dad867fee9","outcome":"completed","summary":"Updated [launch.json](/home/bc/.identitymd/work/d929e590-1c91-448f-ab52-8f3097741578/1c1195cf-dc50-4b5a-a49e-b7dad867fee9/launch.json) to describe the implemented fees and batch buybacks.\n\nSchema, constructor arguments, and permissions validated. `forge build` passed; `forge test`: 68 passed, 0 failed, 2 fork suites skipped without a configured fork.\n\nOnly `launch.json` changed.","treeHash":"fbd25591aaa24d732299c64707c3eee54bedbce1","usage":{"cachedInputTokens":471936,"inputTokens":65242,"model":"gpt-6-astra","outputTokens":4174,"runtime":"codex","turns":4,"wallClockMs":176660}},{"artifacts":[],"attempt":1,"bundleHash":"8b8dccbf4df26dad9328739ba1f0462c531e2b286240604826622c35e9874b25","device":"b0ab61c6f4146d8d","findings":[],"hash":"a5b17f40dee1a4337d9e1c097cf5a11b3829803a363cc05546cab6c449e02d09","nodeId":"1c1195cf-dc50-4b5a-a49e-b7dad867fee9","outcome":"completed","summary":"Updated [launch.json](/root/.identitymd/work/d929e590-1c91-448f-ab52-8f3097741578/1c1195cf-dc50-4b5a-a49e-b7dad867fee9/launch.json) to describe the implemented fees, permissions, constructor arguments, and buybacks.\n\nValidation passed against the supplied schema and implementation. `forge build` passed; `forge test` reported 102 passed, 0 failed, and 2 skipped because no mainnet fork was configured.\n\nOnly `launch.json` changed.","treeHash":"2c785acf36ff97d6b951f18f1c7dbf2774dd63d0","usage":{"cachedInputTokens":293888,"inputTokens":45178,"model":"gpt-6-astra","outputTokens":4126,"runtime":"codex","turns":3,"wallClockMs":165238}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"e57a8e639cccfbab","findings":[{"citation":"resolved","description":"Execution trace: afterSwap() -> _observe() records whatever slot0.tick the PoolManager left after the swap, with no regard to whether any liquidity exists at that tick. The launch factory seeds the pool with launched tokens only (a one-sided position above the opening price when the token is currency0, below it when the token is currency1), so the sell side of the opening price has zero liquidity. In v4, a swap through a zero-liquidity region fills nothing but still walks sqrtPriceX96 to the swapper's sqrtPriceLimitX96 (Pool.swap loops until the limit is reached). A seller who overshoots the IMD the pool holds, using the router-default limit MIN_SQRT_PRICE+1 / MAX_SQRT_PRICE-1, therefore parks slot0.tick at -887272 / +887271 at zero cost: amount0 == amount1 == 0, no hook fee, gas only. At launch (price at the position edge, no IMD in the pool yet) a 1-wei sell does it. The hook records that tick and weights it by elapsed time: referencePrice() = TickMath.getSqrtPriceAtTick(floor(sum(tick_i * dt_i) / elapsed)). After only 12 seconds of a 3600-second epoch the mean is (-887272*12 + 0*3588)/3600 = -2958 ticks, i.e. a reference ~26% below spot even though spot was inside the seeded range for 3588 of 3600 seconds. executeBatch() then computes limitX96 = reference*sqrt(1.03) (or *sqrt(0.97)), which lies on the far side of spot, and `room` is false: budget stays unspent, lastBatch is set, the hourly slot is consumed and the epoch is reset. The void is always on the sell side of the one-sided position, so the poisoned reference can only tighten the hook's band, never loosen it: the impact is liveness, not overpayment. Variant: if spot is still in the void when the batch runs, `room` is true and the hook's own exact-input swap also fills zero while moving the pool price 300 bps toward the limit (observed in the quiet-pool probe: batches with spent == 0 still changed sqrtPriceX96), re-anchoring the epoch in the void; the hook then walks the price ~296 ticks per hour until a buyer restores it. First principles: the buyback exists to buy while the price sits at or near the launch floor, which is exactly when sells overshoot the pool's IMD and poison the tick. Any seller with a loose limit (not just a griefer) triggers it, it costs nothing extra, it recurs every time the price returns to the floor, and each occurrence disables the batch for the remainder of that epoch plus the next full hour. The guarantee \"the buyback can never deadlock\" holds only in the narrow sense that it resumes after an hour with no void excursion. Suggested direction (author's call): do not sample slot0.tick when poolManager.getLiquidity(poolId) == 0 (freeze the last in-liquidity tick, or carry the previous observation at zero weight), or clamp observations to the tick range of the seeded position; and skip the epoch reset / slot consumption when the batch swap filled zero.","line":248,"path":"src/SIMDTESTHook.sol","reproduction":"State: pool initialized at sqrtPriceX96 = 2^96 (tick 0); token-only position [0, 600] when token is currency0 (or [-600, 0] when currency1) with liquidity 10_000_000e18; hook holds 1000e18 IMD directly (pending() == 1000e18). Calls: (1) t0: router.swap(key, SwapParams(zeroForOne = tokenIsCurrency0, amountSpecified = -1, sqrtPriceLimitX96 = tokenIsCurrency0 ? MIN_SQRT_PRICE+1 : MAX_SQRT_PRICE-1)) -> returns delta (0,0), seller balances unchanged, slot0.tick == -887272 (token is currency0) or +887271 (currency1). (2) t0+12: a buyer swaps exact-input 100e18 IMD with a loose limit -> fills, slot0.tick back to 0 / -1. (3) t0+3600: hook.referencePrice() corresponds to tick -2958 / +2956; hook.executeBatch() returns (spent = 0, burned = 0), lastBatch == t0+3600, next executeBatch reverts BatchTooSoon for an hour. Expected: with pending 1000e18, spot inside the seeded range and 10_000_000e18 liquidity at spot, the batch spends its 250e18 budget (the control test without step (1) returns spent == 250e18 in both orderings). Actual: spent == 0 and the hourly slot is consumed. Reproduced by test/scratch/VoidTickPoisonsReference.t.sol in both currency orderings (control passes, defect test fails with \"buyback did not spend although budget, in-range spot and liquidity exist\").","severity":"medium","snippet":"        (, observedTick,,) = poolManager.getSlot0(poolId);","title":"Zero-fill swaps on the empty side of the launch position move slot0.tick into the no-liquidity void; _observe() weights those ticks into referencePrice(), so one 1-wei sell lasting one block makes the"},{"citation":"resolved","description":"After the batch swap, executeBatch() starts the next epoch at the tick the hook's own partial fill left behind (the price-limit tick when the fill was price-limited). With no external trades during the following hour, referencePrice() for the next batch equals exactly that post-batch limit price, so the next limit is another 300 bps beyond it. The \"only slippage guard\" is therefore relative to a reference the hook itself moves: with pending() large relative to pool depth the hook walks the launched token's price up by ~3% per hour indefinitely using only its own flow, and any holder can sell into each successively higher bid. This is a consequence of the chosen epoch definition rather than a coding error; it is reported because the band is described as a slippage guard and it does not bound cumulative slippage across batches. A reference that carries the pre-batch TWAP forward (or excludes the hook's own fill from the new epoch's opening observation) would bound the drift.","line":211,"path":"src/SIMDTESTHook.sol","reproduction":"State: pool at tick 0 with liquidity 10_000_000e18 in [-600, 600]; hook holds 50_000_000e18 IMD directly (budget 12_500_000e18 each hour). No external swaps. Token is currency0 ordering. Hour 1: executeBatch() -> referenceX96 = 79228162514264337593543950336, spent = 150776268447817174683545 (price-limited partial fill), post-batch sqrtPriceX96 = 80407803025877290634728829553 (token price +2.99%). Hour 2: referencePrice() = 80405379686066038741618617935 (the hour-1 limit), spent = 152706847896785892380662, post-batch price +6.07% vs launch. Hour 3: spent = 4901576069703334570946 and the price reaches +9.25% (the seeded range ends at tick 600). Expected under a market-anchored reference: the hook should not be able to pay 3% more each hour with no external trade. Actual: each hour's limit is 300 bps beyond the previous hour's own fill price.","severity":"low","snippet":"        (, observedTick,,) = poolManager.getSlot0(poolId);","title":"Each batch re-anchors the reference epoch at the post-batch tick, so in a quiet pool the 300 bps band compounds on the hook's own price impact hour after hour"},{"citation":"resolved","description":"Mechanics item M1 says the batch swaps \"with a sqrtPriceLimit 300 bps beyond a time-weighted reference price kept by the hook\", and referencePrice() is itself a sqrtPriceX96. The implementation instead moves the sqrt price by sqrt(1.03) - 1 = 1.489% (sqrt(0.97) = 0.98489), i.e. 300 bps in squared price. The README documents this as a deliberate interpretation. It is tighter than the literal reading, so it cannot cause overpayment, but every price-limited batch fills roughly half of what the literal 300 bps sqrt-price band would allow and the unfilled remainder waits for the next hour. Reported for the author/judge to confirm which reading the brief intends; if the literal reading is intended the factors become 0.97e18 / 1.03e18.","line":262,"path":"src/SIMDTESTHook.sol","reproduction":"State: referencePrice() == 79228162514264337593543950336 (tick 0). Token is currency0 (hook buys oneForZero): _priceLimit returns 79228162514264337593543950336 * 1014889156509221946 / 1e18 = 80407803025877290634728829553. Literal \"sqrtPriceLimit 300 bps beyond the reference\" = 79228162514264337593543950336 * 103 / 100 = 81605007389692267721350268846. Token is currency1 (zeroForOne): implemented ceil(ref * 984885780179610473 / 1e18) = 78030690650058201001108743414 vs literal ref * 97 / 100 = 76851317638836407465737631825. In the quiet-pool run above, a batch with budget 12_500_000e18 against 10_000_000e18 liquidity filled 150_776e18 at the implemented limit; under the literal limit it would fill about twice that.","severity":"low","snippet":"        uint256 limit = pairedIsCurrency0\n            ? FullMath.mulDivRoundingUp(referenceX96, 984885780179610473, 1e18)\n            : FullMath.mulDiv(referenceX96, 1014889156509221946, 1e18);","title":"The 300 bps band is applied to pool price (sqrt factors 0.98489 / 1.01489), not to the sqrtPriceLimit as the mechanics text literally states, halving the per-batch fill the brief describes"}],"hash":"cf3616e2d6467a76c4d1b64d7d7266d6fde6b9fb96e107e4c9144d493c1f65e5","nodeId":"a647c337-f7e6-47c2-80e3-e313f767697d","outcome":"completed","summary":"Review complete. The findings file is written and validated, the working tree has no stray files, and the only scratch artifact is the gitignored proof test.\n\n**Outcome.** The hook's control flow is largely sound: callback authorization, fee-from-actual-delta accounting, claim minting and delta conservation, the batch's partial-fill settlement, the operation gate on `unlockCallback`, and the sweep path all traced clean in both currency orderings. One real defect surfaced in the reference-price flow, plus two lower-severity design observations.\n\n**Findings written to `.imd-findings.json`:**\n\n1. **Medium, with proof.** A swap into the no-liquidity side of the token-only launch position fills nothing but still parks the pool tick at the swapper's price limit. The hook's observation step records that tick unconditionally and time-weights it. After one 12-second excursion in a 3600-second epoch the reference lands about 2957 ticks away from spot, the room check fails, and the batch spends zero while consuming the hourly slot. A 1 wei sell at launch state does this for gas only, and any seller who overshoots the pool's IMD with a router-default limit does it unintentionally. The poisoned reference can only tighten the band, so the impact is a stalled buyback rather than overpayment. The proof test fails on the current code in both orderings and includes a passing control.\n\n2. **Low.** Each batch re-anchors the epoch at its own post-fill tick. In a quiet pool the next hour's limit is 300 bps beyond the previous fill, so the hook walks the price up about 3% per hour on its own flow. Numbers from a 50M IMD pending run are in the reproduction.\n\n3. **Low.** The 300 bps band is applied to pool price, giving sqrt factors of 0.98489 and 1.01489, while the mechanics text literally says the sqrtPriceLimit sits 300 bps beyond the reference. Tighter than the literal reading, documented in the README, flagged for the judge to settle intent.\n\n**Coverage.** All eight listed entry points have rows, plus three invariant rows. Three rows point at finding 1. Unreached: nothing within the hook. The mainnet fork rehearsal could not be run here, and I did not exercise liquidity add or remove mid-epoch beyond what the existing suite does.","treeHash":null,"usage":{"cachedInputTokens":3428862,"inputTokens":674,"model":"claude-fable-5-1","outputTokens":68555,"runtime":"claude","turns":39,"wallClockMs":1008918}},{"artifacts":[],"attempt":1,"bundleHash":"0f1c8e684bea13d6bcc6190294d60817fbdc2b0e0dc1dac0f6cff6ba9dda13a6","device":"308998a1407291a8","findings":[],"hash":"de34b0cffb20bc3a2e03c4bd2cbcfae1754a615af284b558cf7b65fa5436be88","nodeId":"8bb012d3-ee80-4328-9349-a2579b0ddb85","outcome":"completed","summary":"Implemented the Foundry project with SIMDTEST, the immutable fee hook, sweeps, bounded batch buybacks, launch manifest, vendored dependencies, and deployment documentation.\n\nChecks passed:\n\n- `forge build`\n- `forge fmt --check`\n- `forge test`: 42 passed, including fuzz and conservation tests.\n\nMainnet fork tests are included but skipped locally because public RPC requests returned HTTP 403.","treeHash":"44f6f655eeea24295918b99d26eb76c19ea64a46","usage":{"cachedInputTokens":1398528,"inputTokens":114830,"model":"gpt-6-astra","outputTokens":27592,"runtime":"codex","turns":6,"wallClockMs":1089929}}],"verification":[{"checks":[{"durationMs":7012,"exitCode":0,"name":"build","output":"Compiling 87 files with Solc 0.8.26\nSolc 0.8.26 finished in 6.84s\nCompiler run successful!\nwarning[missing-events-arithmetic]: `observedTick` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:113:9\n    │\n113 │         observedTick = TickMath.getTickAtSqrtPrice(sqrtPriceX96);\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedAt` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:114:9\n    │\n114 │         observedAt = block.timestamp;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `epochStart` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:115:9\n    │\n115 │         epochStart = block.timestamp;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `lastBatch` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:116:9\n    │\n116 │         lastBatch = block.timestamp;\n    │         ━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `cumulativeTick` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:257:9\n    │\n257 │         cumulativeTick += int256(observedTick) * int256(block.timestamp - observedAt);\n    │         ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedAt` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:258:9\n    │\n258 │         observedAt = block.timestamp;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedTick` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:261:52\n    │\n261 │         if (poolManager.getLiquidity(poolId) != 0) (, observedTick,,) = poolManager.getSlot0(poolId);\n    │                                                    ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/SIMDTESTHook.sol:64:40\n   │\n64 │     constructor(IPoolManager manager_, address token_) {\n   │                                        ━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:130:19\n    │\n130 │                 ? uint256(-params.amountSpecified)\n    │                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:131:19\n    │\n131 │                 : uint256(params.amountSpecified);\n    │                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:133:25\n    │\n133 │         if (magnitude > uint256(type(int256).max) - magnitude / 100) revert UnrepresentableFee();\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SIMDTESTHook.sol:155:13\n    │\n155 │             emit FeeAccrued(Currency.unwrap(currency), amount);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:149:26\n    │\n149 │         uint256 amount = uint256(filled < 0 ? -filled : filled) / 100;\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:151:15\n    │\n151 │         fee = int128(int256(amount));\n    │               ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:151:22\n    │\n151 │         fee = int128(int256(amount));\n    │                      ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SIMDTESTHook.sol:177:13\n    │\n177 │         if (elapsed != 0) {\n    │             ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SIMDTESTHook.sol:180:28\n    │\n180 │             if (sum < 0 && sum % int256(elapsed) != 0) --mean;\n    │                            ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:178:66\n    │\n178 │             int256 sum = cumulativeTick + int256(observedTick) * int256(block.timestamp - observedAt);\n    │                                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:179:26\n    │\n179 │             mean = sum / int256(elapsed);\n    │                          ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:180:34\n    │\n180 │             if (sum < 0 && sum % int256(elapsed) != 0) --mean;\n    │                                  ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:182:44\n    │\n182 │         return TickMath.getSqrtPriceAtTick(int24(mean));\n    │                                            ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int24' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `operation` is updated\n    ╭▸ src/SIMDTESTHook.sol:190:26\n    │\n190 │         if (claims != 0) poolManager.unlock(abi.encode(claims));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SIMDTESTHook.sol:192:9\n    │\n192 │         emit Swept(amount);\n    │         ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/SIMDTESTHook.sol:190:26\n    │\n190 │         if (claims != 0) poolManager.unlock(abi.encode(claims));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-no-eth]: external call can be reentered before `lastBatch` is updated\n    ╭▸ src/SIMDTESTHook.sol:215:42\n    │\n215 │             (spent, burned) = abi.decode(poolManager.unlock(abi.encode(budget, limitX96)), (uint256, uint256));\n    │                                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SIMDTESTHook.sol:198:13\n    │\n198 │         if (block.timestamp - lastBatch < BATCH_INTERVAL) revert BatchTooSoon();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SIMDTESTHook.sol:226:9\n    │\n226 │         emit BatchExecuted(budget, spent, burned, referenceX96, limitX96);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:212:22\n    │\n212 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:212:30\n    │\n212 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:212:67\n    │\n212 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                                                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:212:75\n    │\n212 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                                                                           ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:239:72\n    │\n239 │             poolManager.swap(poolKey(), SwapParams(pairedIsCurrency0, -int256(budget), limit), \"\");\n    │                                                                        ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:240:25\n    │\n240 │         uint256 spent = uint256(-int256(pairedIsCurrency0 ? delta.amount0() : delta.amount1()));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:241:26\n    │\n241 │         uint256 bought = uint256(int256(pairedIsCurrency0 ? delta.amount1() : delta.amount0()));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/SIMDTESTHook.sol:250:13\n    │\n250 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:257:50\n    │\n257 │         cumulativeTick += int256(observedTick) * int256(block.timestamp - observedAt);\n    │                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:280:16\n    │\n280 │         return uint160(limit);\n    │                ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/BatchRegression.t.sol:32:17\n   │\n32 │         vm.warp(block.timestamp + 1);\n   │         ────────━━━━━━━━━━━━━━━───── `vm.warp` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockTimestamp()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/BatchRegression.t.sol:48:25\n   │\n48 │         uint256 start = block.timestamp;\n   │                         ━━━━━━━━━━━━━━━\n49 │         vm.warp(start + 1);\n   │         ────────────────── `vm.warp` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockTimestamp()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/BatchRegression.t.sol:65:17\n   │\n65 │         vm.warp(block.timestamp + 1);\n   │         ────────━━━━━━━━━━━━━━━───── `vm.warp` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockTimestamp()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/BatchRegression.t.sol:79:25\n   │\n79 │         uint256 start = block.timestamp;\n   │                         ━━━━━━━━━━━━━━━\n80 │         vm.warp(start + 3600);\n   │         ───────────────────── `vm.warp` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockTimestamp()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/BatchRegression.t.sol:186:25\n    │\n186 │         uint256 start = block.timestamp;\n    │                         ━━━━━━━━━━━━━━━\n187 │         vm.warp(start + 3600);\n    │         ───────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/BatchRegression.t.sol:212:25\n    │\n212 │         uint256 start = block.timestamp;\n    │                         ━━━━━━━━━━━━━━━\n213 │         vm.warp(start + 3600);\n    │         ───────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/BatchRegression.t.sol:241:17\n    │\n241 │         vm.warp(block.timestamp + 3600);\n    │         ────────━━━━━━━━━━━━━━━──────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/SIMDTESTHook.t.sol:131:17\n    │\n131 │         vm.warp(block.timestamp + 3600);\n    │         ────────━━━━━━━━━━━━━━━──────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/SIMDTESTHook.t.sol:147:17\n    │\n147 │         vm.warp(block.timestamp + 3600);\n    │         ────────━━━━━━━━━━━━━━━──────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/SIMDTESTHook.t.sol:171:33\n    │\n171 │         uint256 initializedAt = block.timestamp;\n    │                                 ━━━━━━━━━━━━━━━\n172 │         vm.warp(initializedAt + 3599);\n    │         ───────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":4070,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/MainnetFork.t.sol:MainnetForkToken1Test\n[SKIP: skipped: Supply a mainnet fork via forge --fork-url to rehearse against deployed contracts] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 661.55µs (0.00ns CPU time)\n\nRan 1 test for test/MainnetFork.t.sol:MainnetForkTest\n[SKIP: skipped: Supply a mainnet fork via forge --fork-url to rehearse against deployed contracts] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 666.83µs (0.00ns CPU time)\n\nRan 5 tests for test/DeploymentBoundaries.t.sol:DeploymentBoundariesTest\n[PASS] test_ConstructorRejectsNonContractsAndIdenticalCurrencies() (gas: 7598)\n[PASS] test_Create2RejectsAddressWithIncorrectPermissionBits() (gas: 23011)\n[PASS] test_UninitializedBatchFailsButSweepAndLaterInitializationWork() (gas: 526348)\n[PASS] test_UnrepresentableFeeBoundaryForBothSpecifiedSigns() (gas: 85153)\n[PASS] test_WrongPoolBeforeFirstInitializationCannotPoisonLaunch() (gas: 580919)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 13.39ms (1.10ms CPU time)\n\nRan 2 tests for test/SIMDTESTHook.t.sol:FreshManagerTest\n[PASS] test_TokenOnlyLiquidityExactOutputBuyCollectsIMDFeeBeforeInputArrives() (gas: 770790)\n[PASS] test_ZeroLiquiditySwapAndBatchHaveZeroFeesAndNoDeadlock() (gas: 430868)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 14.17ms (1.05ms CPU time)\n\nRan 16 tests for test/BatchRegression.t.sol:BatchRegressionToken1Test\n[PASS] testFuzz_DriftedBatchRespectsSpotBandAndBudget(uint24,uint16) (runs: 1000, μ: 838066, ~: 836453)\n[PASS] test_CoreTickBoundaryConventionIsPreserved() (gas: 763209)\n[PASS] test_DriftedReferenceCannotAllowMoreThanThreePercentSpotImpact() (gas: 822764)\nLogs:\n  price ratio (1e18): 970000000000000001\n\n[PASS] test_DustFeesRoundDown() (gas: 26394679)\n[PASS] test_EmptyPoolAttemptsPreserveReferenceAndSlot() (gas: 330909)\n[PASS] test_FractionalMeanTickRoundsDown() (gas: 532021)\n[PASS] test_LargeSandwichAfterDriftLosesMoney() (gas: 1164045)\nLogs:\n  sandwich profit in IMD wei: -6060400525509601185178\n\n[PASS] test_NonatomicDeploymentAllowsFirstInitializer() (gas: 36406548)\n[PASS] test_NonzeroFillThatExhaustsLiquidityDoesNotRecordVoidTick() (gas: 1692805)\n[PASS] test_QuietPoolReanchorsAtEachFilledBatch() (gas: 776907)\n[PASS] test_RepeatedOutOfBandAttemptsPreserveEpochAndHourlySlot() (gas: 845466)\n[PASS] test_SandwichWithoutDriftControlLosesMoney() (gas: 984601)\nLogs:\n  sandwich profit in IMD wei: -1240180979413197579140\n\n[PASS] test_SmallSandwichAfterDriftLosesMoney() (gas: 1163321)\nLogs:\n  sandwich profit in IMD wei: -673009625310132996892\n\n[PASS] test_VoidExcursionThenBuyerRestoresSpot() (gas: 1521145)\n[PASS] test_ZeroFillGriefCannotConsumeSlotOrResetTwap() (gas: 1336427)\n[PASS] test_ZeroFillVoidDoesNotPoisonReferenceOrBlockBatch() (gas: 1384994)\nSuite result: ok. 16 passed; 0 failed; 0 skipped; finished in 274.50ms (333.03ms CPU time)\n\nRan 10 tests for test/AdversarialHook.t.sol:AdversarialToken1Test\n[PASS] testFuzz_FailedSettlementRollsBackClaimsAndOracle(bool,uint256) (runs: 1000, μ: 997467, ~: 1012120)\n[PASS] testFuzz_HugeRequestsStillChargeOnlyActualPartialFill(bool,bool,uint256) (runs: 1000, μ: 336472, ~: 338584)\n[PASS] test_BackedClaimDonationsAreIncludedAndCannotBeStolen() (gas: 1424017)\n[PASS] test_DustSwapsAroundOnePercentRoundingBoundary() (gas: 6439439)\n[PASS] test_FailedBatchRestoresBudgetOracleAndCooldownForRetry() (gas: 799189)\n[PASS] test_FailedSweepRestoresClaimsAndAllowsImmediateRetry() (gas: 573623)\n[PASS] test_MixedClaimsAndDirectIMDSettleOnlyWhatBatchSpends() (gas: 773547)\n[PASS] test_NestedSweepAndBatchCannotRedeemDuringAnotherUnlock() (gas: 1454006)\n[PASS] test_PartialBatchPreservesUnspentClaimsForNextHour() (gas: 1458133)\n[PASS] test_SubFourWeiBudgetStaysAccruedAndDoesNotBlockSweeping() (gas: 787477)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 274.80ms (403.00ms CPU time)\n\nRan 3 tests for test/SIMDTEST.t.sol:SIMDTESTTest\n[PASS] testFuzz_TransferAndAllowanceConserveSupply(uint256) (runs: 1000, μ: 169784, ~: 170167)\n[PASS] test_FactoryReceivesEntirePolicySupply() (gas: 44103)\n[PASS] test_NoMintOwnerUpgradeOrPauseEntrypoints() (gas: 145216)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 283.06ms (283.07ms CPU time)\n\nRan 16 tests for test/BatchRegression.t.sol:BatchRegressionToken0Test\n[PASS] testFuzz_DriftedBatchRespectsSpotBandAndBudget(uint24,uint16) (runs: 1000, μ: 842734, ~: 842329)\n[PASS] test_CoreTickBoundaryConventionIsPreserved() (gas: 763222)\n[PASS] test_DriftedReferenceCannotAllowMoreThanThreePercentSpotImpact() (gas: 828824)\nLogs:\n  price ratio (1e18): 1029999999999999997\n\n[PASS] test_DustFeesRoundDown() (gas: 26500959)\n[PASS] test_EmptyPoolAttemptsPreserveReferenceAndSlot() (gas: 324657)\n[PASS] test_FractionalMeanTickRoundsDown() (gas: 532034)\n[PASS] test_LargeSandwichAfterDriftLosesMoney() (gas: 1171274)\nLogs:\n  sandwich profit in IMD wei: -6426335962633652393075\n\n[PASS] test_NonatomicDeploymentAllowsFirstInitializer() (gas: 6181688)\n[PASS] test_NonzeroFillThatExhaustsLiquidityDoesNotRecordVoidTick() (gas: 1681455)\n[PASS] test_QuietPoolReanchorsAtEachFilledBatch() (gas: 743262)\n[PASS] test_RepeatedOutOfBandAttemptsPreserveEpochAndHourlySlot() (gas: 838502)\n[PASS] test_SandwichWithoutDriftControlLosesMoney() (gas: 978477)\nLogs:\n  sandwich profit in IMD wei: -1284636568137345946979\n\n[PASS] test_SmallSandwichAfterDriftLosesMoney() (gas: 1171359)\nLogs:\n  sandwich profit in IMD wei: -713637653871767692221\n\n[PASS] test_VoidExcursionThenBuyerRestoresSpot() (gas: 1517985)\n[PASS] test_ZeroFillGriefCannotConsumeSlotOrResetTwap() (gas: 1317008)\n[PASS] test_ZeroFillVoidDoesNotPoisonReferenceOrBlockBatch() (gas: 1382414)\nSuite result: ok. 16 passed; 0 failed; 0 skipped; finished in 283.11ms (316.43ms CPU time)\n\nRan 18 tests for test/SIMDTESTHook.t.sol:TokenIsCurrency1Test\n[PASS] testFuzz_FeeMatchesRealFill(bool,bool,uint256) (runs: 1000, μ: 324544, ~: 331311)\n[PASS] testFuzz_PartialFillOnlyTaxesFilledUnspecifiedCurrency(bool,bool,uint24) (runs: 1000, μ: 336417, ~: 338545)\n[PASS] test_AllCallbacksRejectUnauthorizedCallers() (gas: 141735)\n[PASS] test_AnyoneSweepsTokenFeesImmediatelyAndIdempotently() (gas: 677558)\n[PASS] test_BatchPriceLimitAcceptsPartialFillAndRemainderLater() (gas: 555789)\n[PASS] test_BatchUses25PercentBurnsOutputAndChargesNoHookFee() (gas: 706848)\n[PASS] test_CannotBatchInsideSomeoneElsesUnlock() (gas: 78089)\n[PASS] test_CooldownBoundaryAndEmptyBatch() (gas: 523862)\n[PASS] test_ExactInputBuy() (gas: 336054)\n[PASS] test_ExactInputSell() (gas: 329548)\n[PASS] test_ExactOutputBuy() (gas: 335164)\n[PASS] test_ExactOutputSell() (gas: 330094)\n[PASS] test_OutsideLimitSkipsThenRecoversWithoutSpending() (gas: 566353)\n[PASS] test_PermissionBitsAndInitialization() (gas: 41675)\n[PASS] test_RejectsOtherPoolsAndReinitialization() (gas: 126177)\n[PASS] test_RuntimeAndCreationSizeAndNoEscapeOpcodes() (gas: 2743646)\n[PASS] test_TimeWeightedReferenceExcludesSameTimestampManipulation() (gas: 492259)\n[PASS] test_UnrepresentableSpecifiedAmountRefused() (gas: 91061)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 283.07ms (487.80ms CPU time)\n\nRan 18 tests for test/SIMDTESTHook.t.sol:TokenIsCurrency0Test\n[PASS] testFuzz_FeeMatchesRealFill(bool,bool,uint256) (runs: 1000, μ: 324384, ~: 331281)\n[PASS] testFuzz_PartialFillOnlyTaxesFilledUnspecifiedCurrency(bool,bool,uint24) (runs: 1000, μ: 336404, ~: 333658)\n[PASS] test_AllCallbacksRejectUnauthorizedCallers() (gas: 141735)\n[PASS] test_AnyoneSweepsTokenFeesImmediatelyAndIdempotently() (gas: 681057)\n[PASS] test_BatchPriceLimitAcceptsPartialFillAndRemainderLater() (gas: 522021)\n[PASS] test_BatchUses25PercentBurnsOutputAndChargesNoHookFee() (gas: 712631)\n[PASS] test_CannotBatchInsideSomeoneElsesUnlock() (gas: 78089)\n[PASS] test_CooldownBoundaryAndEmptyBatch() (gas: 496816)\n[PASS] test_ExactInputBuy() (gas: 329544)\n[PASS] test_ExactInputSell() (gas: 336058)\n[PASS] test_ExactOutputBuy() (gas: 330093)\n[PASS] test_ExactOutputSell() (gas: 335165)\n[PASS] test_OutsideLimitSkipsThenRecoversWithoutSpending() (gas: 556380)\n[PASS] test_PermissionBitsAndInitialization() (gas: 41675)\n[PASS] test_RejectsOtherPoolsAndReinitialization() (gas: 126177)\n[PASS] test_RuntimeAndCreationSizeAndNoEscapeOpcodes() (gas: 2743646)\n[PASS] test_TimeWeightedReferenceExcludesSameTimestampManipulation() (gas: 486281)\n[PASS] test_UnrepresentableSpecifiedAmountRefused() (gas: 91079)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 283.04ms (447.15ms CPU time)\n\nRan 10 tests for test/AdversarialHook.t.sol:AdversarialToken0Test\n[PASS] testFuzz_FailedSettlementRollsBackClaimsAndOracle(bool,uint256) (runs: 1000, μ: 972609, ~: 988553)\n[PASS] testFuzz_HugeRequestsStillChargeOnlyActualPartialFill(bool,bool,uint256) (runs: 1000, μ: 336458, ~: 333969)\n[PASS] test_BackedClaimDonationsAreIncludedAndCannotBeStolen() (gas: 1397303)\n[PASS] test_DustSwapsAroundOnePercentRoundingBoundary() (gas: 6425324)\n[PASS] test_FailedBatchRestoresBudgetOracleAndCooldownForRetry() (gas: 804843)\n[PASS] test_FailedSweepRestoresClaimsAndAllowsImmediateRetry() (gas: 567155)\n[PASS] test_MixedClaimsAndDirectIMDSettleOnlyWhatBatchSpends() (gas: 767559)\n[PASS] test_NestedSweepAndBatchCannotRedeemDuringAnotherUnlock() (gas: 1430159)\n[PASS] test_PartialBatchPreservesUnspentClaimsForNextHour() (gas: 1423823)\n[PASS] test_SubFourWeiBudgetStaysAccruedAndDoesNotBlockSweeping() (gas: 760431)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 283.26ms (574.91ms CPU time)\n\nRan 2 tests for test/Conservation.invariant.t.sol:ConservationInvariantTest\n[PASS] invariant_AllFeeAssetsAreAccountedFor() (runs: 256, calls: 16384, reverts: 0)\n\n╭---------------+--------------+-------+---------+----------╮\n| Contract      | Selector     | Calls | Reverts | Discards |\n+===========================================================+\n| LaunchHandler | batch        | 2682  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | donate       | 2751  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | elapse       | 2732  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | limitedTrade | 2769  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | sweep        | 2735  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | trade        | 2715  | 0       | 0        |\n╰---------------+--------------+-------+---------+----------╯\n\n[PASS] test_HandlerEmptyAndDustAttemptsRetainHistoryUntilFundedRetry() (gas: 2053905)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 3.98s (3.96s CPU time)\n\nRan 2 tests for test/Conservation.invariant.t.sol:ConservationToken1InvariantTest\n[PASS] invariant_AllFeeAssetsAreAccountedFor() (runs: 256, calls: 16384, reverts: 0)\n\n╭---------------+--------------+-------+---------+----------╮\n| Contract      | Selector     | Calls | Reverts | Discards |\n+===========================================================+\n| LaunchHandler | batch        | 2682  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | donate       | 2751  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | elapse       | 2732  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | limitedTrade | 2769  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | sweep        | 2735  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | trade        | 2715  | 0       | 0        |\n╰---------------+--------------+-------+---------+----------╯\n\n[PASS] test_HandlerEmptyAndDustAttemptsRetainHistoryUntilFundedRetry() (gas: 2064671)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 3.98s (3.94s CPU time)\n\nRan 13 test suites in 3.98s (9.95s CPU time): 102 tests passed, 0 failed, 2 skipped (104 total tests)\n","passed":true},{"durationMs":59,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"SIMDTEST.approve(address,uint256)\",\"SIMDTEST.transfer(address,uint256)\",\"SIMDTEST.transferFrom(address,address,uint256)\",\"SIMDTESTHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"SIMDTESTHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"SIMDTESTHook.executeBatch()\",\"SIMDTESTHook.sweep()\",\"SIMDTESTHook.unlockCallback(bytes)\"],\"files\":{\".gitignore\":4,\"LICENSE\":23,\"README.md\":237,\"dependencies.json\":19,\"foundry.toml\":20,\"launch.json\":16,\"remappings.txt\":4,\"script/MineHook.s.sol\":28,\"src/HookFlags.sol\":29,\"src/SIMDTEST.sol\":11,\"src/SIMDTESTHook.sol\":282,\"test/AdversarialHook.t.sol\":299,\"test/BatchRegression.t.sol\":297,\"test/Conservation.invariant.t.sol\":269,\"test/DeploymentBoundaries.t.sol\":117,\"test/MainnetFork.t.sol\":32,\"test/README.md\":36,\"test/SIMDTEST.t.sol\":53,\"test/SIMDTESTHook.t.sol\":285,\"test/helpers/ClaimActions.sol\":49,\"test/helpers/LaunchFixture.sol\":133,\"test/helpers/PoolActor.sol\":81,\"test/helpers/SwapEvidence.sol\":40,\"test/mocks/MockERC20.sol\":15},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"377196a274179129f153947115e1951654906b9dc1c91a17fae5fb95a5f6d500","verifiedTreeHash":"34340478e25309e1277e6f84a469395d80b15f23","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":5610,"exitCode":0,"name":"build","output":"Compiling 83 files with Solc 0.8.26\nSolc 0.8.26 finished in 5.32s\nCompiler run successful!\nwarning[missing-events-arithmetic]: `observedTick` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:113:9\n    │\n113 │         observedTick = TickMath.getTickAtSqrtPrice(sqrtPriceX96);\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedAt` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:114:9\n    │\n114 │         observedAt = block.timestamp;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `epochStart` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:115:9\n    │\n115 │         epochStart = block.timestamp;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `lastBatch` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:116:9\n    │\n116 │         lastBatch = block.timestamp;\n    │         ━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `cumulativeTick` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:257:9\n    │\n257 │         cumulativeTick += int256(observedTick) * int256(block.timestamp - observedAt);\n    │         ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedAt` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:258:9\n    │\n258 │         observedAt = block.timestamp;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedTick` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:261:52\n    │\n261 │         if (poolManager.getLiquidity(poolId) != 0) (, observedTick,,) = poolManager.getSlot0(poolId);\n    │                                                    ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/SIMDTESTHook.sol:64:40\n   │\n64 │     constructor(IPoolManager manager_, address token_) {\n   │                                        ━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:130:19\n    │\n130 │                 ? uint256(-params.amountSpecified)\n    │                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:131:19\n    │\n131 │                 : uint256(params.amountSpecified);\n    │                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:133:25\n    │\n133 │         if (magnitude > uint256(type(int256).max) - magnitude / 100) revert UnrepresentableFee();\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SIMDTESTHook.sol:155:13\n    │\n155 │             emit FeeAccrued(Currency.unwrap(currency), amount);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:149:26\n    │\n149 │         uint256 amount = uint256(filled < 0 ? -filled : filled) / 100;\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:151:15\n    │\n151 │         fee = int128(int256(amount));\n    │               ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:151:22\n    │\n151 │         fee = int128(int256(amount));\n    │                      ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SIMDTESTHook.sol:177:13\n    │\n177 │         if (elapsed != 0) {\n    │             ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SIMDTESTHook.sol:180:28\n    │\n180 │             if (sum < 0 && sum % int256(elapsed) != 0) --mean;\n    │                            ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:178:66\n    │\n178 │             int256 sum = cumulativeTick + int256(observedTick) * int256(block.timestamp - observedAt);\n    │                                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:179:26\n    │\n179 │             mean = sum / int256(elapsed);\n    │                          ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:180:34\n    │\n180 │             if (sum < 0 && sum % int256(elapsed) != 0) --mean;\n    │                                  ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:182:44\n    │\n182 │         return TickMath.getSqrtPriceAtTick(int24(mean));\n    │                                            ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int24' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `operation` is updated\n    ╭▸ src/SIMDTESTHook.sol:190:26\n    │\n190 │         if (claims != 0) poolManager.unlock(abi.encode(claims));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SIMDTESTHook.sol:192:9\n    │\n192 │         emit Swept(amount);\n    │         ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/SIMDTESTHook.sol:190:26\n    │\n190 │         if (claims != 0) poolManager.unlock(abi.encode(claims));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-no-eth]: external call can be reentered before `lastBatch` is updated\n    ╭▸ src/SIMDTESTHook.sol:215:42\n    │\n215 │             (spent, burned) = abi.decode(poolManager.unlock(abi.encode(budget, limitX96)), (uint256, uint256));\n    │                                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SIMDTESTHook.sol:198:13\n    │\n198 │         if (block.timestamp - lastBatch < BATCH_INTERVAL) revert BatchTooSoon();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SIMDTESTHook.sol:226:9\n    │\n226 │         emit BatchExecuted(budget, spent, burned, referenceX96, limitX96);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:212:22\n    │\n212 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:212:30\n    │\n212 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:212:67\n    │\n212 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                                                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:212:75\n    │\n212 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                                                                           ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:239:72\n    │\n239 │             poolManager.swap(poolKey(), SwapParams(pairedIsCurrency0, -int256(budget), limit), \"\");\n    │                                                                        ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:240:25\n    │\n240 │         uint256 spent = uint256(-int256(pairedIsCurrency0 ? delta.amount0() : delta.amount1()));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:241:26\n    │\n241 │         uint256 bought = uint256(int256(pairedIsCurrency0 ? delta.amount1() : delta.amount0()));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/SIMDTESTHook.sol:250:13\n    │\n250 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:257:50\n    │\n257 │         cumulativeTick += int256(observedTick) * int256(block.timestamp - observedAt);\n    │                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:280:16\n    │\n280 │         return uint160(limit);\n    │                ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/BatchRegression.t.sol:32:17\n   │\n32 │         vm.warp(block.timestamp + 1);\n   │         ────────━━━━━━━━━━━━━━━───── `vm.warp` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockTimestamp()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/BatchRegression.t.sol:48:25\n   │\n48 │         uint256 start = block.timestamp;\n   │                         ━━━━━━━━━━━━━━━\n49 │         vm.warp(start + 1);\n   │         ────────────────── `vm.warp` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockTimestamp()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/BatchRegression.t.sol:65:17\n   │\n65 │         vm.warp(block.timestamp + 1);\n   │         ────────━━━━━━━━━━━━━━━───── `vm.warp` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockTimestamp()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/BatchRegression.t.sol:79:25\n   │\n79 │         uint256 start = block.timestamp;\n   │                         ━━━━━━━━━━━━━━━\n80 │         vm.warp(start + 3600);\n   │         ───────────────────── `vm.warp` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockTimestamp()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/BatchRegression.t.sol:186:25\n    │\n186 │         uint256 start = block.timestamp;\n    │                         ━━━━━━━━━━━━━━━\n187 │         vm.warp(start + 3600);\n    │         ───────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/BatchRegression.t.sol:212:25\n    │\n212 │         uint256 start = block.timestamp;\n    │                         ━━━━━━━━━━━━━━━\n213 │         vm.warp(start + 3600);\n    │         ───────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/BatchRegression.t.sol:241:17\n    │\n241 │         vm.warp(block.timestamp + 3600);\n    │         ────────━━━━━━━━━━━━━━━──────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/SIMDTESTHook.t.sol:131:17\n    │\n131 │         vm.warp(block.timestamp + 3600);\n    │         ────────━━━━━━━━━━━━━━━──────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/SIMDTESTHook.t.sol:147:17\n    │\n147 │         vm.warp(block.timestamp + 3600);\n    │         ────────━━━━━━━━━━━━━━━──────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/SIMDTESTHook.t.sol:171:33\n    │\n171 │         uint256 initializedAt = block.timestamp;\n    │                                 ━━━━━━━━━━━━━━━\n172 │         vm.warp(initializedAt + 3599);\n    │         ───────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":1410,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/MainnetFork.t.sol:MainnetForkTest\n[SKIP: skipped: Supply a mainnet fork via forge --fork-url to rehearse against deployed contracts] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 1.04ms (0.00ns CPU time)\n\nRan 2 tests for test/SIMDTESTHook.t.sol:FreshManagerTest\n[PASS] test_TokenOnlyLiquidityExactOutputBuyCollectsIMDFeeBeforeInputArrives() (gas: 770790)\n[PASS] test_ZeroLiquiditySwapAndBatchHaveZeroFeesAndNoDeadlock() (gas: 430868)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 12.96ms (1.39ms CPU time)\n\nRan 3 tests for test/SIMDTEST.t.sol:SIMDTESTTest\n[PASS] testFuzz_TransferAndAllowanceConserveSupply(uint256) (runs: 1000, μ: 169870, ~: 170167)\n[PASS] test_FactoryReceivesEntirePolicySupply() (gas: 44103)\n[PASS] test_NoMintOwnerUpgradeOrPauseEntrypoints() (gas: 145216)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 181.45ms (181.43ms CPU time)\n\nRan 16 tests for test/BatchRegression.t.sol:BatchRegressionToken0Test\n[PASS] testFuzz_DriftedBatchRespectsSpotBandAndBudget(uint24,uint16) (runs: 1000, μ: 842804, ~: 842336)\n[PASS] test_CoreTickBoundaryConventionIsPreserved() (gas: 763222)\n[PASS] test_DriftedReferenceCannotAllowMoreThanThreePercentSpotImpact() (gas: 828824)\nLogs:\n  price ratio (1e18): 1029999999999999997\n\n[PASS] test_DustFeesRoundDown() (gas: 26500959)\n[PASS] test_EmptyPoolAttemptsPreserveReferenceAndSlot() (gas: 324657)\n[PASS] test_FractionalMeanTickRoundsDown() (gas: 532034)\n[PASS] test_LargeSandwichAfterDriftLosesMoney() (gas: 1171274)\nLogs:\n  sandwich profit in IMD wei: -6426335962633652393075\n\n[PASS] test_NonatomicDeploymentAllowsFirstInitializer() (gas: 6181688)\n[PASS] test_NonzeroFillThatExhaustsLiquidityDoesNotRecordVoidTick() (gas: 1681455)\n[PASS] test_QuietPoolReanchorsAtEachFilledBatch() (gas: 743262)\n[PASS] test_RepeatedOutOfBandAttemptsPreserveEpochAndHourlySlot() (gas: 838502)\n[PASS] test_SandwichWithoutDriftControlLosesMoney() (gas: 978477)\nLogs:\n  sandwich profit in IMD wei: -1284636568137345946979\n\n[PASS] test_SmallSandwichAfterDriftLosesMoney() (gas: 1171359)\nLogs:\n  sandwich profit in IMD wei: -713637653871767692221\n\n[PASS] test_VoidExcursionThenBuyerRestoresSpot() (gas: 1517985)\n[PASS] test_ZeroFillGriefCannotConsumeSlotOrResetTwap() (gas: 1317008)\n[PASS] test_ZeroFillVoidDoesNotPoisonReferenceOrBlockBatch() (gas: 1382414)\nSuite result: ok. 16 passed; 0 failed; 0 skipped; finished in 181.53ms (223.00ms CPU time)\n\nRan 16 tests for test/BatchRegression.t.sol:BatchRegressionToken1Test\n[PASS] testFuzz_DriftedBatchRespectsSpotBandAndBudget(uint24,uint16) (runs: 1000, μ: 837924, ~: 836271)\n[PASS] test_CoreTickBoundaryConventionIsPreserved() (gas: 763209)\n[PASS] test_DriftedReferenceCannotAllowMoreThanThreePercentSpotImpact() (gas: 822764)\nLogs:\n  price ratio (1e18): 970000000000000001\n\n[PASS] test_DustFeesRoundDown() (gas: 26394679)\n[PASS] test_EmptyPoolAttemptsPreserveReferenceAndSlot() (gas: 330909)\n[PASS] test_FractionalMeanTickRoundsDown() (gas: 532021)\n[PASS] test_LargeSandwichAfterDriftLosesMoney() (gas: 1164045)\nLogs:\n  sandwich profit in IMD wei: -6060400525509601185178\n\n[PASS] test_NonatomicDeploymentAllowsFirstInitializer() (gas: 36406548)\n[PASS] test_NonzeroFillThatExhaustsLiquidityDoesNotRecordVoidTick() (gas: 1692805)\n[PASS] test_QuietPoolReanchorsAtEachFilledBatch() (gas: 776907)\n[PASS] test_RepeatedOutOfBandAttemptsPreserveEpochAndHourlySlot() (gas: 845466)\n[PASS] test_SandwichWithoutDriftControlLosesMoney() (gas: 984601)\nLogs:\n  sandwich profit in IMD wei: -1240180979413197579140\n\n[PASS] test_SmallSandwichAfterDriftLosesMoney() (gas: 1163321)\nLogs:\n  sandwich profit in IMD wei: -673009625310132996892\n\n[PASS] test_VoidExcursionThenBuyerRestoresSpot() (gas: 1521145)\n[PASS] test_ZeroFillGriefCannotConsumeSlotOrResetTwap() (gas: 1336427)\n[PASS] test_ZeroFillVoidDoesNotPoisonReferenceOrBlockBatch() (gas: 1384994)\nSuite result: ok. 16 passed; 0 failed; 0 skipped; finished in 181.53ms (254.54ms CPU time)\n\nRan 18 tests for test/SIMDTESTHook.t.sol:TokenIsCurrency1Test\n[PASS] testFuzz_FeeMatchesRealFill(bool,bool,uint256) (runs: 1000, μ: 324466, ~: 331298)\n[PASS] testFuzz_PartialFillOnlyTaxesFilledUnspecifiedCurrency(bool,bool,uint24) (runs: 1000, μ: 336399, ~: 333699)\n[PASS] test_AllCallbacksRejectUnauthorizedCallers() (gas: 141735)\n[PASS] test_AnyoneSweepsTokenFeesImmediatelyAndIdempotently() (gas: 677558)\n[PASS] test_BatchPriceLimitAcceptsPartialFillAndRemainderLater() (gas: 555789)\n[PASS] test_BatchUses25PercentBurnsOutputAndChargesNoHookFee() (gas: 706848)\n[PASS] test_CannotBatchInsideSomeoneElsesUnlock() (gas: 78089)\n[PASS] test_CooldownBoundaryAndEmptyBatch() (gas: 523862)\n[PASS] test_ExactInputBuy() (gas: 336054)\n[PASS] test_ExactInputSell() (gas: 329548)\n[PASS] test_ExactOutputBuy() (gas: 335164)\n[PASS] test_ExactOutputSell() (gas: 330094)\n[PASS] test_OutsideLimitSkipsThenRecoversWithoutSpending() (gas: 566353)\n[PASS] test_PermissionBitsAndInitialization() (gas: 41675)\n[PASS] test_RejectsOtherPoolsAndReinitialization() (gas: 126177)\n[PASS] test_RuntimeAndCreationSizeAndNoEscapeOpcodes() (gas: 2743646)\n[PASS] test_TimeWeightedReferenceExcludesSameTimestampManipulation() (gas: 492259)\n[PASS] test_UnrepresentableSpecifiedAmountRefused() (gas: 91061)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 181.52ms (255.25ms CPU time)\n\nRan 18 tests for test/SIMDTESTHook.t.sol:TokenIsCurrency0Test\n[PASS] testFuzz_FeeMatchesRealFill(bool,bool,uint256) (runs: 1000, μ: 324221, ~: 331263)\n[PASS] testFuzz_PartialFillOnlyTaxesFilledUnspecifiedCurrency(bool,bool,uint24) (runs: 1000, μ: 336449, ~: 338582)\n[PASS] test_AllCallbacksRejectUnauthorizedCallers() (gas: 141735)\n[PASS] test_AnyoneSweepsTokenFeesImmediatelyAndIdempotently() (gas: 681057)\n[PASS] test_BatchPriceLimitAcceptsPartialFillAndRemainderLater() (gas: 522021)\n[PASS] test_BatchUses25PercentBurnsOutputAndChargesNoHookFee() (gas: 712631)\n[PASS] test_CannotBatchInsideSomeoneElsesUnlock() (gas: 78089)\n[PASS] test_CooldownBoundaryAndEmptyBatch() (gas: 496816)\n[PASS] test_ExactInputBuy() (gas: 329544)\n[PASS] test_ExactInputSell() (gas: 336058)\n[PASS] test_ExactOutputBuy() (gas: 330093)\n[PASS] test_ExactOutputSell() (gas: 335165)\n[PASS] test_OutsideLimitSkipsThenRecoversWithoutSpending() (gas: 556380)\n[PASS] test_PermissionBitsAndInitialization() (gas: 41675)\n[PASS] test_RejectsOtherPoolsAndReinitialization() (gas: 126177)\n[PASS] test_RuntimeAndCreationSizeAndNoEscapeOpcodes() (gas: 2743646)\n[PASS] test_TimeWeightedReferenceExcludesSameTimestampManipulation() (gas: 486281)\n[PASS] test_UnrepresentableSpecifiedAmountRefused() (gas: 91079)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 181.73ms (317.90ms CPU time)\n\nRan 1 test for test/Conservation.invariant.t.sol:ConservationInvariantTest\n[PASS] invariant_AllFeeAssetsAreAccountedFor() (runs: 256, calls: 8192, reverts: 0)\n\n╭---------------+----------+-------+---------+----------╮\n| Contract      | Selector | Calls | Reverts | Discards |\n+=======================================================+\n| LaunchHandler | batch    | 2081  | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| LaunchHandler | elapse   | 2050  | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| LaunchHandler | sweep    | 2051  | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| LaunchHandler | trade    | 2010  | 0       | 0        |\n╰---------------+----------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.30s (1.28s CPU time)\n\nRan 8 test suites in 1.30s (2.22s CPU time): 74 tests passed, 0 failed, 1 skipped (75 total tests)\n","passed":true},{"durationMs":67,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"SIMDTEST.approve(address,uint256)\",\"SIMDTEST.transfer(address,uint256)\",\"SIMDTEST.transferFrom(address,address,uint256)\",\"SIMDTESTHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"SIMDTESTHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"SIMDTESTHook.executeBatch()\",\"SIMDTESTHook.sweep()\",\"SIMDTESTHook.unlockCallback(bytes)\"],\"files\":{\".gitignore\":4,\"LICENSE\":23,\"README.md\":237,\"dependencies.json\":19,\"foundry.toml\":20,\"launch.json\":16,\"remappings.txt\":4,\"script/MineHook.s.sol\":28,\"src/HookFlags.sol\":29,\"src/SIMDTEST.sol\":11,\"src/SIMDTESTHook.sol\":282,\"test/BatchRegression.t.sol\":297,\"test/Conservation.invariant.t.sol\":90,\"test/MainnetFork.t.sol\":17,\"test/SIMDTEST.t.sol\":53,\"test/SIMDTESTHook.t.sol\":285,\"test/helpers/LaunchFixture.sol\":129,\"test/helpers/PoolActor.sol\":81,\"test/mocks/MockERC20.sol\":15},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":1864,"exitCode":0,"name":"slither","output":"[high/medium] weak-prng at src/SIMDTESTHook.sol:173: SIMDTESTHook.referencePrice() (src/SIMDTESTHook.sol#173-183) uses a weak PRNG: \"sum < 0 && sum % int256(elapsed) != 0 (src/SIMDTESTHook.sol#180)\"\n[medium/medium] reentrancy-no-eth at src/SIMDTESTHook.sol:196: Reentrancy in SIMDTESTHook.executeBatch() (src/SIMDTESTHook.sol#196-227):\n[medium/medium] unused-return at src/SIMDTESTHook.sol:196: SIMDTESTHook.executeBatch() (src/SIMDTESTHook.sol#196-227) ignores return value by (None,observedTick,None,None) = poolManager.getSlot0(poolId) (src/SIMDTESTHook.sol#221)\n[medium/medium] unused-return at src/SIMDTESTHook.sol:186: SIMDTESTHook.sweep() (src/SIMDTESTHook.sol#186-193) ignores return value by poolManager.unlock(abi.encode(claims)) (src/SIMDTESTHook.sol#190)\n[medium/medium] unused-return at src/SIMDTESTHook.sol:256: SIMDTESTHook._observe() (src/SIMDTESTHook.sol#256-262) ignores return value by (None,observedTick,None,None) = poolManager.getSlot0(poolId) (src/SIMDTESTHook.sol#261)\n[medium/medium] unused-return at src/SIMDTESTHook.sol:196: SIMDTESTHook.executeBatch() (src/SIMDTESTHook.sol#196-227) ignores return value by (spot,None,None,None) = poolManager.getSlot0(poolId) (src/SIMDTESTHook.sol#201)\n[medium/medium] unused-return at src/SIMDTESTHook.sol:229: SIMDTESTHook.unlockCallback(bytes) (src/SIMDTESTHook.sol#229-254) ignores return value by poolManager.settle() (src/SIMDTESTHook.sol#250)\n[low/medium] events-maths at src/SIMDTESTHook.sol:104: SIMDTESTHook.beforeInitialize(address,PoolKey,uint160) (src/SIMDTESTHook.sol#104-118) should emit an event for: \n[low/medium] reentrancy-events at src/SIMDTESTHook.sol:137: Reentrancy in SIMDTESTHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/SIMDTESTHook.sol#137-158):\n[low/medium] reentrancy-events at src/SIMDTESTHook.sol:196: Reentrancy in SIMDTESTHook.executeBatch() (src/SIMDTESTHook.sol#196-227):\n[low/medium] reentrancy-events at src/SIMDTESTHook.sol:186: Reentrancy in SIMDTESTHook.sweep() (src/SIMDTESTHook.sol#186-193):\n[low/medium] timestamp at src/SIMDTESTHook.sol:173: SIMDTESTHook.referencePrice() (src/SIMDTESTHook.sol#173-183) uses timestamp for comparisons\n[low/medium] timestamp at src/SIMDTESTHook.sol:272: SIMDTESTHook._priceLimit(uint160) (src/SIMDTESTHook.sol#272-281) uses timestamp for comparisons\n[low/medium] timestamp at src/SIMDTESTHook.sol:196: SIMDTESTHook.executeBatch() (src/SIMDTESTHook.sol#196-227) uses timestamp for comparisons","passed":true},{"durationMs":442,"exitCode":0,"name":"aderyn","output":"[high] reentrancy-state-change at src/SIMDTESTHook.sol:215: Reentrancy: State change after external call\n[high] unsafe-casting at src/SIMDTESTHook.sol:182: Unsafe Casting of integers\n[low] internal-function-used-once at src/HookFlags.sol:22: Internal Function Used Only Once\n[low] large-numeric-literal at src/SIMDTEST.sol:9: Large Numeric Literal\n[low] literal-instead-of-constant at src/SIMDTESTHook.sol:133: Literal Instead of Constant (4 places)\n[low] state-change-without-event at src/SIMDTESTHook.sol:104: State Change Without Event\n[low] unchecked-return at src/SIMDTESTHook.sol:250: Unchecked Return\n[low] unsafe-erc20-operation at src/SIMDTESTHook.sol:191: Unsafe ERC20 Operation (2 places)\n[low] unused-public-function at src/SIMDTESTHook.sol:166: Public Function Not Used Internally","passed":true},{"durationMs":2510,"exitCode":0,"name":"proof 079c6b427339","output":"Compiling 79 files with Solc 0.8.26\nSolc 0.8.26 finished in 1.81s\nCompiler run successful!\n\nRan 1 test for test/imd-proof-da476310/Proof_079c6b427339.t.sol:ZeroFillReferenceProof\n[PASS] test_zeroFillSwapDoesNotStallTheBuyback() (gas: 1129904)\nLogs:\n  reference tick used: 60\n  spent: 250000000000000000000\n  burned: 245392218516200505871\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 11.39ms (1.31ms CPU time)\n\nRan 1 test suite in 11.99ms (11.39ms 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":"42bb6187237a6942ed38b71b89bd506223ab178de7531a712ed7d75ecd45e85b","verifiedTreeHash":"f825a4b94de6cb7c41dd088e638119ec5af3ab88","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":6412,"exitCode":0,"name":"build","output":"Compiling 86 files with Solc 0.8.26\nSolc 0.8.26 finished in 6.22s\nCompiler run successful!\nwarning[missing-events-arithmetic]: `observedTick` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:113:9\n    │\n113 │         observedTick = TickMath.getTickAtSqrtPrice(sqrtPriceX96);\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedAt` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:114:9\n    │\n114 │         observedAt = block.timestamp;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `epochStart` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:115:9\n    │\n115 │         epochStart = block.timestamp;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `lastBatch` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:116:9\n    │\n116 │         lastBatch = block.timestamp;\n    │         ━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `cumulativeTick` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:246:9\n    │\n246 │         cumulativeTick += int256(observedTick) * int256(block.timestamp - observedAt);\n    │         ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedAt` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:247:9\n    │\n247 │         observedAt = block.timestamp;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedTick` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:248:9\n    │\n248 │         (, observedTick,,) = poolManager.getSlot0(poolId);\n    │         ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/SIMDTESTHook.sol:64:40\n   │\n64 │     constructor(IPoolManager manager_, address token_) {\n   │                                        ━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:130:19\n    │\n130 │                 ? uint256(-params.amountSpecified)\n    │                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:131:19\n    │\n131 │                 : uint256(params.amountSpecified);\n    │                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:133:25\n    │\n133 │         if (magnitude > uint256(type(int256).max) - magnitude / 100) revert UnrepresentableFee();\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SIMDTESTHook.sol:155:13\n    │\n155 │             emit FeeAccrued(Currency.unwrap(currency), amount);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:149:26\n    │\n149 │         uint256 amount = uint256(filled < 0 ? -filled : filled) / 100;\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:151:15\n    │\n151 │         fee = int128(int256(amount));\n    │               ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:151:22\n    │\n151 │         fee = int128(int256(amount));\n    │                      ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SIMDTESTHook.sol:177:13\n    │\n177 │         if (elapsed != 0) {\n    │             ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SIMDTESTHook.sol:180:28\n    │\n180 │             if (sum < 0 && sum % int256(elapsed) != 0) --mean;\n    │                            ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:178:66\n    │\n178 │             int256 sum = cumulativeTick + int256(observedTick) * int256(block.timestamp - observedAt);\n    │                                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:179:26\n    │\n179 │             mean = sum / int256(elapsed);\n    │                          ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:180:34\n    │\n180 │             if (sum < 0 && sum % int256(elapsed) != 0) --mean;\n    │                                  ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:182:44\n    │\n182 │         return TickMath.getSqrtPriceAtTick(int24(mean));\n    │                                            ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int24' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `operation` is updated\n    ╭▸ src/SIMDTESTHook.sol:190:26\n    │\n190 │         if (claims != 0) poolManager.unlock(abi.encode(claims));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SIMDTESTHook.sol:192:9\n    │\n192 │         emit Swept(amount);\n    │         ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/SIMDTESTHook.sol:190:26\n    │\n190 │         if (claims != 0) poolManager.unlock(abi.encode(claims));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-no-eth]: external call can be reentered before `observedTick` is updated\n    ╭▸ src/SIMDTESTHook.sol:208:42\n    │\n208 │             (spent, burned) = abi.decode(poolManager.unlock(abi.encode(budget, limitX96)), (uint256, uint256));\n    │                                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SIMDTESTHook.sol:198:13\n    │\n198 │         if (block.timestamp - lastBatch < BATCH_INTERVAL) revert BatchTooSoon();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SIMDTESTHook.sol:215:9\n    │\n215 │         emit BatchExecuted(budget, spent, burned, referenceX96, limitX96);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:203:22\n    │\n203 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:203:30\n    │\n203 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:203:67\n    │\n203 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                                                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:203:75\n    │\n203 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                                                                           ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:228:72\n    │\n228 │             poolManager.swap(poolKey(), SwapParams(pairedIsCurrency0, -int256(budget), limit), \"\");\n    │                                                                        ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:229:25\n    │\n229 │         uint256 spent = uint256(-int256(pairedIsCurrency0 ? delta.amount0() : delta.amount1()));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:230:26\n    │\n230 │         uint256 bought = uint256(int256(pairedIsCurrency0 ? delta.amount1() : delta.amount0()));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/SIMDTESTHook.sol:239:13\n    │\n239 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:246:50\n    │\n246 │         cumulativeTick += int256(observedTick) * int256(block.timestamp - observedAt);\n    │                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:267:16\n    │\n267 │         return uint160(limit);\n    │                ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/SIMDTESTHook.t.sol:131:17\n    │\n131 │         vm.warp(block.timestamp + 3600);\n    │         ────────━━━━━━━━━━━━━━━──────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/SIMDTESTHook.t.sol:147:17\n    │\n147 │         vm.warp(block.timestamp + 3600);\n    │         ────────━━━━━━━━━━━━━━━──────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/SIMDTESTHook.t.sol:171:33\n    │\n171 │         uint256 initializedAt = block.timestamp;\n    │                                 ━━━━━━━━━━━━━━━\n172 │         vm.warp(initializedAt + 3599);\n    │         ───────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":4061,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/MainnetFork.t.sol:MainnetForkTest\n[SKIP: skipped: Supply a mainnet fork via forge --fork-url to rehearse against deployed contracts] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 672.72µs (0.00ns CPU time)\n\nRan 1 test for test/MainnetFork.t.sol:MainnetForkToken1Test\n[SKIP: skipped: Supply a mainnet fork via forge --fork-url to rehearse against deployed contracts] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 651.60µs (0.00ns CPU time)\n\nRan 5 tests for test/DeploymentBoundaries.t.sol:DeploymentBoundariesTest\n[PASS] test_ConstructorRejectsNonContractsAndIdenticalCurrencies() (gas: 7598)\n[PASS] test_Create2RejectsAddressWithIncorrectPermissionBits() (gas: 22287)\n[PASS] test_UninitializedBatchFailsButSweepAndLaterInitializationWork() (gas: 530989)\n[PASS] test_UnrepresentableFeeBoundaryForBothSpecifiedSigns() (gas: 85153)\n[PASS] test_WrongPoolBeforeFirstInitializationCannotPoisonLaunch() (gas: 580823)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 54.19ms (770.19µs CPU time)\n\nRan 2 tests for test/SIMDTESTHook.t.sol:FreshManagerTest\n[PASS] test_TokenOnlyLiquidityExactOutputBuyCollectsIMDFeeBeforeInputArrives() (gas: 766741)\n[PASS] test_ZeroLiquiditySwapAndBatchHaveZeroFeesAndNoDeadlock() (gas: 461647)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 90.77ms (1.26ms CPU time)\n\nRan 18 tests for test/SIMDTESTHook.t.sol:TokenIsCurrency0Test\n[PASS] testFuzz_FeeMatchesRealFill(bool,bool,uint256) (runs: 1000, μ: 322128, ~: 330051)\n[PASS] testFuzz_PartialFillOnlyTaxesFilledUnspecifiedCurrency(bool,bool,uint24) (runs: 1000, μ: 335104, ~: 332445)\n[PASS] test_AllCallbacksRejectUnauthorizedCallers() (gas: 141711)\n[PASS] test_AnyoneSweepsTokenFeesImmediatelyAndIdempotently() (gas: 678643)\n[PASS] test_BatchPriceLimitAcceptsPartialFillAndRemainderLater() (gas: 516337)\n[PASS] test_BatchUses25PercentBurnsOutputAndChargesNoHookFee() (gas: 707393)\n[PASS] test_CannotBatchInsideSomeoneElsesUnlock() (gas: 78077)\n[PASS] test_CooldownBoundaryAndEmptyBatch() (gas: 193299)\n[PASS] test_ExactInputBuy() (gas: 328343)\n[PASS] test_ExactInputSell() (gas: 334857)\n[PASS] test_ExactOutputBuy() (gas: 328892)\n[PASS] test_ExactOutputSell() (gas: 333964)\n[PASS] test_OutsideLimitSkipsThenRecoversWithoutSpending() (gas: 555381)\n[PASS] test_PermissionBitsAndInitialization() (gas: 41675)\n[PASS] test_RejectsOtherPoolsAndReinitialization() (gas: 126141)\n[PASS] test_RuntimeAndCreationSizeAndNoEscapeOpcodes() (gas: 2652632)\n[PASS] test_TimeWeightedReferenceExcludesSameTimestampManipulation() (gas: 483879)\n[PASS] test_UnrepresentableSpecifiedAmountRefused() (gas: 91067)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 246.79ms (384.29ms CPU time)\n\nRan 10 tests for test/AdversarialHook.t.sol:AdversarialToken0Test\n[PASS] testFuzz_FailedSettlementRollsBackClaimsAndOracle(bool,uint256) (runs: 1000, μ: 968777, ~: 967884)\n[PASS] testFuzz_HugeRequestsStillChargeOnlyActualPartialFill(bool,bool,uint256) (runs: 1000, μ: 335166, ~: 332547)\n[PASS] test_BackedClaimDonationsAreIncludedAndCannotBeStolen() (gas: 1394419)\n[PASS] test_DustSwapsAroundOnePercentRoundingBoundary() (gas: 6396500)\n[PASS] test_FailedBatchRestoresBudgetOracleAndCooldownForRetry() (gas: 802074)\n[PASS] test_FailedSweepRestoresClaimsAndAllowsImmediateRetry() (gas: 565942)\n[PASS] test_MixedClaimsAndDirectIMDSettleOnlyWhatBatchSpends() (gas: 762309)\n[PASS] test_NestedSweepAndBatchCannotRedeemDuringAnotherUnlock() (gas: 1424897)\n[PASS] test_PartialBatchPreservesUnspentClaimsForNextHour() (gas: 1416938)\n[PASS] test_SubFourWeiBudgetStaysAccruedAndDoesNotBlockSweeping() (gas: 376092)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 247.35ms (293.24ms CPU time)\n\nRan 18 tests for test/SIMDTESTHook.t.sol:TokenIsCurrency1Test\n[PASS] testFuzz_FeeMatchesRealFill(bool,bool,uint256) (runs: 1000, μ: 322669, ~: 330111)\n[PASS] testFuzz_PartialFillOnlyTaxesFilledUnspecifiedCurrency(bool,bool,uint24) (runs: 1000, μ: 335364, ~: 337384)\n[PASS] test_AllCallbacksRejectUnauthorizedCallers() (gas: 141735)\n[PASS] test_AnyoneSweepsTokenFeesImmediatelyAndIdempotently() (gas: 675180)\n[PASS] test_BatchPriceLimitAcceptsPartialFillAndRemainderLater() (gas: 554151)\n[PASS] test_BatchUses25PercentBurnsOutputAndChargesNoHookFee() (gas: 701522)\n[PASS] test_CannotBatchInsideSomeoneElsesUnlock() (gas: 78089)\n[PASS] test_CooldownBoundaryAndEmptyBatch() (gas: 193382)\n[PASS] test_ExactInputBuy() (gas: 334865)\n[PASS] test_ExactInputSell() (gas: 328359)\n[PASS] test_ExactOutputBuy() (gas: 333975)\n[PASS] test_ExactOutputSell() (gas: 328905)\n[PASS] test_OutsideLimitSkipsThenRecoversWithoutSpending() (gas: 565191)\n[PASS] test_PermissionBitsAndInitialization() (gas: 41675)\n[PASS] test_RejectsOtherPoolsAndReinitialization() (gas: 126177)\n[PASS] test_RuntimeAndCreationSizeAndNoEscapeOpcodes() (gas: 2652632)\n[PASS] test_TimeWeightedReferenceExcludesSameTimestampManipulation() (gas: 489881)\n[PASS] test_UnrepresentableSpecifiedAmountRefused() (gas: 91061)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 247.43ms (432.18ms CPU time)\n\nRan 10 tests for test/AdversarialHook.t.sol:AdversarialToken1Test\n[PASS] testFuzz_FailedSettlementRollsBackClaimsAndOracle(bool,uint256) (runs: 1000, μ: 992988, ~: 982227)\n[PASS] testFuzz_HugeRequestsStillChargeOnlyActualPartialFill(bool,bool,uint256) (runs: 1000, μ: 335420, ~: 337462)\n[PASS] test_BackedClaimDonationsAreIncludedAndCannotBeStolen() (gas: 1421077)\n[PASS] test_DustSwapsAroundOnePercentRoundingBoundary() (gas: 6410903)\n[PASS] test_FailedBatchRestoresBudgetOracleAndCooldownForRetry() (gas: 796220)\n[PASS] test_FailedSweepRestoresClaimsAndAllowsImmediateRetry() (gas: 572434)\n[PASS] test_MixedClaimsAndDirectIMDSettleOnlyWhatBatchSpends() (gas: 768221)\n[PASS] test_NestedSweepAndBatchCannotRedeemDuringAnotherUnlock() (gas: 1448680)\n[PASS] test_PartialBatchPreservesUnspentClaimsForNextHour() (gas: 1455283)\n[PASS] test_SubFourWeiBudgetStaysAccruedAndDoesNotBlockSweeping() (gas: 376199)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 247.44ms (473.41ms CPU time)\n\nRan 3 tests for test/SIMDTEST.t.sol:SIMDTESTTest\n[PASS] testFuzz_TransferAndAllowanceConserveSupply(uint256) (runs: 1000, μ: 170098, ~: 170167)\n[PASS] test_FactoryReceivesEntirePolicySupply() (gas: 44103)\n[PASS] test_NoMintOwnerUpgradeOrPauseEntrypoints() (gas: 145216)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 247.45ms (247.45ms CPU time)\n\nRan 1 test for test/Conservation.invariant.t.sol:ConservationToken1InvariantTest\n[PASS] invariant_AllFeeAssetsAreAccountedFor() (runs: 256, calls: 16384, reverts: 0)\n\n╭---------------+--------------+-------+---------+----------╮\n| Contract      | Selector     | Calls | Reverts | Discards |\n+===========================================================+\n| LaunchHandler | batch        | 2701  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | donate       | 2696  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | elapse       | 2772  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | limitedTrade | 2705  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | sweep        | 2757  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | trade        | 2753  | 0       | 0        |\n╰---------------+--------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.91s (3.89s CPU time)\n\nRan 1 test for test/Conservation.invariant.t.sol:ConservationInvariantTest\n[PASS] invariant_AllFeeAssetsAreAccountedFor() (runs: 256, calls: 16384, reverts: 0)\n\n╭---------------+--------------+-------+---------+----------╮\n| Contract      | Selector     | Calls | Reverts | Discards |\n+===========================================================+\n| LaunchHandler | batch        | 2701  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | donate       | 2696  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | elapse       | 2772  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | limitedTrade | 2705  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | sweep        | 2757  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | trade        | 2753  | 0       | 0        |\n╰---------------+--------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.97s (3.88s CPU time)\n\nRan 11 test suites in 3.97s (9.26s CPU time): 68 tests passed, 0 failed, 2 skipped (70 total tests)\n","passed":true},{"durationMs":68,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"SIMDTEST.approve(address,uint256)\",\"SIMDTEST.transfer(address,uint256)\",\"SIMDTEST.transferFrom(address,address,uint256)\",\"SIMDTESTHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"SIMDTESTHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"SIMDTESTHook.executeBatch()\",\"SIMDTESTHook.sweep()\",\"SIMDTESTHook.unlockCallback(bytes)\"],\"files\":{\".gitignore\":4,\"LICENSE\":23,\"README.md\":209,\"dependencies.json\":19,\"foundry.toml\":20,\"launch.json\":16,\"remappings.txt\":4,\"script/MineHook.s.sol\":28,\"src/HookFlags.sol\":29,\"src/SIMDTEST.sol\":11,\"src/SIMDTESTHook.sol\":269,\"test/AdversarialHook.t.sol\":281,\"test/Conservation.invariant.t.sol\":230,\"test/DeploymentBoundaries.t.sol\":117,\"test/MainnetFork.t.sol\":32,\"test/README.md\":35,\"test/SIMDTEST.t.sol\":53,\"test/SIMDTESTHook.t.sol\":278,\"test/helpers/ClaimActions.sol\":49,\"test/helpers/LaunchFixture.sol\":133,\"test/helpers/PoolActor.sol\":81,\"test/helpers/SwapEvidence.sol\":40,\"test/mocks/MockERC20.sol\":15},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"4d5fdff76b9c5856dfbf0f4e564fc252d2f553c2dd174bac5e6419077f8b42d3","verifiedTreeHash":"bceeb3a5cbcf285082362d117e397924f86839f5","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":7193,"exitCode":0,"name":"build","output":"Compiling 86 files with Solc 0.8.26\nSolc 0.8.26 finished in 6.91s\nCompiler run successful!\nwarning[missing-events-arithmetic]: `observedTick` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:113:9\n    │\n113 │         observedTick = TickMath.getTickAtSqrtPrice(sqrtPriceX96);\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedAt` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:114:9\n    │\n114 │         observedAt = block.timestamp;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `epochStart` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:115:9\n    │\n115 │         epochStart = block.timestamp;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `lastBatch` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:116:9\n    │\n116 │         lastBatch = block.timestamp;\n    │         ━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `cumulativeTick` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:246:9\n    │\n246 │         cumulativeTick += int256(observedTick) * int256(block.timestamp - observedAt);\n    │         ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedAt` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:247:9\n    │\n247 │         observedAt = block.timestamp;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedTick` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:248:9\n    │\n248 │         (, observedTick,,) = poolManager.getSlot0(poolId);\n    │         ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/SIMDTESTHook.sol:64:40\n   │\n64 │     constructor(IPoolManager manager_, address token_) {\n   │                                        ━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:130:19\n    │\n130 │                 ? uint256(-params.amountSpecified)\n    │                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:131:19\n    │\n131 │                 : uint256(params.amountSpecified);\n    │                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:133:25\n    │\n133 │         if (magnitude > uint256(type(int256).max) - magnitude / 100) revert UnrepresentableFee();\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SIMDTESTHook.sol:155:13\n    │\n155 │             emit FeeAccrued(Currency.unwrap(currency), amount);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:149:26\n    │\n149 │         uint256 amount = uint256(filled < 0 ? -filled : filled) / 100;\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:151:15\n    │\n151 │         fee = int128(int256(amount));\n    │               ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:151:22\n    │\n151 │         fee = int128(int256(amount));\n    │                      ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SIMDTESTHook.sol:177:13\n    │\n177 │         if (elapsed != 0) {\n    │             ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SIMDTESTHook.sol:180:28\n    │\n180 │             if (sum < 0 && sum % int256(elapsed) != 0) --mean;\n    │                            ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:178:66\n    │\n178 │             int256 sum = cumulativeTick + int256(observedTick) * int256(block.timestamp - observedAt);\n    │                                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:179:26\n    │\n179 │             mean = sum / int256(elapsed);\n    │                          ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:180:34\n    │\n180 │             if (sum < 0 && sum % int256(elapsed) != 0) --mean;\n    │                                  ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:182:44\n    │\n182 │         return TickMath.getSqrtPriceAtTick(int24(mean));\n    │                                            ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int24' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `operation` is updated\n    ╭▸ src/SIMDTESTHook.sol:190:26\n    │\n190 │         if (claims != 0) poolManager.unlock(abi.encode(claims));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SIMDTESTHook.sol:192:9\n    │\n192 │         emit Swept(amount);\n    │         ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/SIMDTESTHook.sol:190:26\n    │\n190 │         if (claims != 0) poolManager.unlock(abi.encode(claims));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-no-eth]: external call can be reentered before `observedTick` is updated\n    ╭▸ src/SIMDTESTHook.sol:208:42\n    │\n208 │             (spent, burned) = abi.decode(poolManager.unlock(abi.encode(budget, limitX96)), (uint256, uint256));\n    │                                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SIMDTESTHook.sol:198:13\n    │\n198 │         if (block.timestamp - lastBatch < BATCH_INTERVAL) revert BatchTooSoon();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SIMDTESTHook.sol:215:9\n    │\n215 │         emit BatchExecuted(budget, spent, burned, referenceX96, limitX96);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:203:22\n    │\n203 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:203:30\n    │\n203 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:203:67\n    │\n203 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                                                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:203:75\n    │\n203 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                                                                           ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:228:72\n    │\n228 │             poolManager.swap(poolKey(), SwapParams(pairedIsCurrency0, -int256(budget), limit), \"\");\n    │                                                                        ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:229:25\n    │\n229 │         uint256 spent = uint256(-int256(pairedIsCurrency0 ? delta.amount0() : delta.amount1()));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:230:26\n    │\n230 │         uint256 bought = uint256(int256(pairedIsCurrency0 ? delta.amount1() : delta.amount0()));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/SIMDTESTHook.sol:239:13\n    │\n239 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:246:50\n    │\n246 │         cumulativeTick += int256(observedTick) * int256(block.timestamp - observedAt);\n    │                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:267:16\n    │\n267 │         return uint160(limit);\n    │                ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/SIMDTESTHook.t.sol:131:17\n    │\n131 │         vm.warp(block.timestamp + 3600);\n    │         ────────━━━━━━━━━━━━━━━──────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/SIMDTESTHook.t.sol:147:17\n    │\n147 │         vm.warp(block.timestamp + 3600);\n    │         ────────━━━━━━━━━━━━━━━──────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/SIMDTESTHook.t.sol:171:33\n    │\n171 │         uint256 initializedAt = block.timestamp;\n    │                                 ━━━━━━━━━━━━━━━\n172 │         vm.warp(initializedAt + 3599);\n    │         ───────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":4410,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/MainnetFork.t.sol:MainnetForkToken1Test\n[SKIP: skipped: Supply a mainnet fork via forge --fork-url to rehearse against deployed contracts] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 1.23ms (0.00ns CPU time)\n\nRan 1 test for test/MainnetFork.t.sol:MainnetForkTest\n[SKIP: skipped: Supply a mainnet fork via forge --fork-url to rehearse against deployed contracts] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 1.81ms (0.00ns CPU time)\n\nRan 5 tests for test/DeploymentBoundaries.t.sol:DeploymentBoundariesTest\n[PASS] test_ConstructorRejectsNonContractsAndIdenticalCurrencies() (gas: 7598)\n[PASS] test_Create2RejectsAddressWithIncorrectPermissionBits() (gas: 22287)\n[PASS] test_UninitializedBatchFailsButSweepAndLaterInitializationWork() (gas: 530989)\n[PASS] test_UnrepresentableFeeBoundaryForBothSpecifiedSigns() (gas: 85153)\n[PASS] test_WrongPoolBeforeFirstInitializationCannotPoisonLaunch() (gas: 580823)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 62.70ms (807.32µs CPU time)\n\nRan 2 tests for test/SIMDTESTHook.t.sol:FreshManagerTest\n[PASS] test_TokenOnlyLiquidityExactOutputBuyCollectsIMDFeeBeforeInputArrives() (gas: 766741)\n[PASS] test_ZeroLiquiditySwapAndBatchHaveZeroFeesAndNoDeadlock() (gas: 461647)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 98.98ms (1.36ms CPU time)\n\nRan 18 tests for test/SIMDTESTHook.t.sol:TokenIsCurrency1Test\n[PASS] testFuzz_FeeMatchesRealFill(bool,bool,uint256) (runs: 1000, μ: 322520, ~: 330108)\n[PASS] testFuzz_PartialFillOnlyTaxesFilledUnspecifiedCurrency(bool,bool,uint24) (runs: 1000, μ: 335280, ~: 337384)\n[PASS] test_AllCallbacksRejectUnauthorizedCallers() (gas: 141735)\n[PASS] test_AnyoneSweepsTokenFeesImmediatelyAndIdempotently() (gas: 675180)\n[PASS] test_BatchPriceLimitAcceptsPartialFillAndRemainderLater() (gas: 554151)\n[PASS] test_BatchUses25PercentBurnsOutputAndChargesNoHookFee() (gas: 701522)\n[PASS] test_CannotBatchInsideSomeoneElsesUnlock() (gas: 78089)\n[PASS] test_CooldownBoundaryAndEmptyBatch() (gas: 193382)\n[PASS] test_ExactInputBuy() (gas: 334865)\n[PASS] test_ExactInputSell() (gas: 328359)\n[PASS] test_ExactOutputBuy() (gas: 333975)\n[PASS] test_ExactOutputSell() (gas: 328905)\n[PASS] test_OutsideLimitSkipsThenRecoversWithoutSpending() (gas: 565191)\n[PASS] test_PermissionBitsAndInitialization() (gas: 41675)\n[PASS] test_RejectsOtherPoolsAndReinitialization() (gas: 126177)\n[PASS] test_RuntimeAndCreationSizeAndNoEscapeOpcodes() (gas: 2652632)\n[PASS] test_TimeWeightedReferenceExcludesSameTimestampManipulation() (gas: 489881)\n[PASS] test_UnrepresentableSpecifiedAmountRefused() (gas: 91061)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 221.18ms (273.40ms CPU time)\n\nRan 3 tests for test/SIMDTEST.t.sol:SIMDTESTTest\n[PASS] testFuzz_TransferAndAllowanceConserveSupply(uint256) (runs: 1000, μ: 169740, ~: 170143)\n[PASS] test_FactoryReceivesEntirePolicySupply() (gas: 44103)\n[PASS] test_NoMintOwnerUpgradeOrPauseEntrypoints() (gas: 145216)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 238.23ms (237.69ms CPU time)\n\nRan 18 tests for test/SIMDTESTHook.t.sol:TokenIsCurrency0Test\n[PASS] testFuzz_FeeMatchesRealFill(bool,bool,uint256) (runs: 1000, μ: 322640, ~: 330072)\n[PASS] testFuzz_PartialFillOnlyTaxesFilledUnspecifiedCurrency(bool,bool,uint24) (runs: 1000, μ: 335174, ~: 332457)\n[PASS] test_AllCallbacksRejectUnauthorizedCallers() (gas: 141711)\n[PASS] test_AnyoneSweepsTokenFeesImmediatelyAndIdempotently() (gas: 678643)\n[PASS] test_BatchPriceLimitAcceptsPartialFillAndRemainderLater() (gas: 516337)\n[PASS] test_BatchUses25PercentBurnsOutputAndChargesNoHookFee() (gas: 707393)\n[PASS] test_CannotBatchInsideSomeoneElsesUnlock() (gas: 78077)\n[PASS] test_CooldownBoundaryAndEmptyBatch() (gas: 193299)\n[PASS] test_ExactInputBuy() (gas: 328343)\n[PASS] test_ExactInputSell() (gas: 334857)\n[PASS] test_ExactOutputBuy() (gas: 328892)\n[PASS] test_ExactOutputSell() (gas: 333964)\n[PASS] test_OutsideLimitSkipsThenRecoversWithoutSpending() (gas: 555381)\n[PASS] test_PermissionBitsAndInitialization() (gas: 41675)\n[PASS] test_RejectsOtherPoolsAndReinitialization() (gas: 126141)\n[PASS] test_RuntimeAndCreationSizeAndNoEscapeOpcodes() (gas: 2652632)\n[PASS] test_TimeWeightedReferenceExcludesSameTimestampManipulation() (gas: 483879)\n[PASS] test_UnrepresentableSpecifiedAmountRefused() (gas: 91067)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 238.20ms (335.55ms CPU time)\n\nRan 10 tests for test/AdversarialHook.t.sol:AdversarialToken0Test\n[PASS] testFuzz_FailedSettlementRollsBackClaimsAndOracle(bool,uint256) (runs: 1000, μ: 967087, ~: 950768)\n[PASS] testFuzz_HugeRequestsStillChargeOnlyActualPartialFill(bool,bool,uint256) (runs: 1000, μ: 335225, ~: 332547)\n[PASS] test_BackedClaimDonationsAreIncludedAndCannotBeStolen() (gas: 1394419)\n[PASS] test_DustSwapsAroundOnePercentRoundingBoundary() (gas: 6396500)\n[PASS] test_FailedBatchRestoresBudgetOracleAndCooldownForRetry() (gas: 802074)\n[PASS] test_FailedSweepRestoresClaimsAndAllowsImmediateRetry() (gas: 565942)\n[PASS] test_MixedClaimsAndDirectIMDSettleOnlyWhatBatchSpends() (gas: 762309)\n[PASS] test_NestedSweepAndBatchCannotRedeemDuringAnotherUnlock() (gas: 1424897)\n[PASS] test_PartialBatchPreservesUnspentClaimsForNextHour() (gas: 1416938)\n[PASS] test_SubFourWeiBudgetStaysAccruedAndDoesNotBlockSweeping() (gas: 376092)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 238.60ms (366.64ms CPU time)\n\nRan 10 tests for test/AdversarialHook.t.sol:AdversarialToken1Test\n[PASS] testFuzz_FailedSettlementRollsBackClaimsAndOracle(bool,uint256) (runs: 1000, μ: 991847, ~: 982225)\n[PASS] testFuzz_HugeRequestsStillChargeOnlyActualPartialFill(bool,bool,uint256) (runs: 1000, μ: 335330, ~: 337426)\n[PASS] test_BackedClaimDonationsAreIncludedAndCannotBeStolen() (gas: 1421077)\n[PASS] test_DustSwapsAroundOnePercentRoundingBoundary() (gas: 6410903)\n[PASS] test_FailedBatchRestoresBudgetOracleAndCooldownForRetry() (gas: 796220)\n[PASS] test_FailedSweepRestoresClaimsAndAllowsImmediateRetry() (gas: 572434)\n[PASS] test_MixedClaimsAndDirectIMDSettleOnlyWhatBatchSpends() (gas: 768221)\n[PASS] test_NestedSweepAndBatchCannotRedeemDuringAnotherUnlock() (gas: 1448680)\n[PASS] test_PartialBatchPreservesUnspentClaimsForNextHour() (gas: 1455283)\n[PASS] test_SubFourWeiBudgetStaysAccruedAndDoesNotBlockSweeping() (gas: 376199)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 238.72ms (466.29ms CPU time)\n\nRan 1 test for test/Conservation.invariant.t.sol:ConservationInvariantTest\n[PASS] invariant_AllFeeAssetsAreAccountedFor() (runs: 256, calls: 16384, reverts: 0)\n\n╭---------------+--------------+-------+---------+----------╮\n| Contract      | Selector     | Calls | Reverts | Discards |\n+===========================================================+\n| LaunchHandler | batch        | 2736  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | donate       | 2709  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | elapse       | 2835  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | limitedTrade | 2781  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | sweep        | 2689  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | trade        | 2634  | 0       | 0        |\n╰---------------+--------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.14s (4.09s CPU time)\n\nRan 1 test for test/Conservation.invariant.t.sol:ConservationToken1InvariantTest\n[PASS] invariant_AllFeeAssetsAreAccountedFor() (runs: 256, calls: 16384, reverts: 0)\n\n╭---------------+--------------+-------+---------+----------╮\n| Contract      | Selector     | Calls | Reverts | Discards |\n+===========================================================+\n| LaunchHandler | batch        | 2736  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | donate       | 2709  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | elapse       | 2835  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | limitedTrade | 2781  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | sweep        | 2689  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | trade        | 2634  | 0       | 0        |\n╰---------------+--------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.30s (4.27s CPU time)\n\nRan 11 test suites in 4.30s (9.78s CPU time): 68 tests passed, 0 failed, 2 skipped (70 total tests)\n","passed":true},{"durationMs":58,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"SIMDTEST.approve(address,uint256)\",\"SIMDTEST.transfer(address,uint256)\",\"SIMDTEST.transferFrom(address,address,uint256)\",\"SIMDTESTHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"SIMDTESTHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"SIMDTESTHook.executeBatch()\",\"SIMDTESTHook.sweep()\",\"SIMDTESTHook.unlockCallback(bytes)\"],\"files\":{\".gitignore\":4,\"LICENSE\":23,\"README.md\":209,\"dependencies.json\":19,\"foundry.toml\":20,\"launch.json\":16,\"remappings.txt\":4,\"script/MineHook.s.sol\":28,\"src/HookFlags.sol\":29,\"src/SIMDTEST.sol\":11,\"src/SIMDTESTHook.sol\":269,\"test/AdversarialHook.t.sol\":281,\"test/Conservation.invariant.t.sol\":230,\"test/DeploymentBoundaries.t.sol\":117,\"test/MainnetFork.t.sol\":32,\"test/README.md\":35,\"test/SIMDTEST.t.sol\":53,\"test/SIMDTESTHook.t.sol\":278,\"test/helpers/ClaimActions.sol\":49,\"test/helpers/LaunchFixture.sol\":133,\"test/helpers/PoolActor.sol\":81,\"test/helpers/SwapEvidence.sol\":40,\"test/mocks/MockERC20.sol\":15},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"a01c5c6b983eff34791034fbcfbeadcc8df90b47da160cca0e819b5b4ef5d9ff","verifiedTreeHash":"fbd25591aaa24d732299c64707c3eee54bedbce1","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":7290,"exitCode":0,"name":"build","output":"Compiling 87 files with Solc 0.8.26\nSolc 0.8.26 finished in 7.12s\nCompiler run successful!\nwarning[missing-events-arithmetic]: `observedTick` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:113:9\n    │\n113 │         observedTick = TickMath.getTickAtSqrtPrice(sqrtPriceX96);\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedAt` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:114:9\n    │\n114 │         observedAt = block.timestamp;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `epochStart` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:115:9\n    │\n115 │         epochStart = block.timestamp;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `lastBatch` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:116:9\n    │\n116 │         lastBatch = block.timestamp;\n    │         ━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `cumulativeTick` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:257:9\n    │\n257 │         cumulativeTick += int256(observedTick) * int256(block.timestamp - observedAt);\n    │         ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedAt` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:258:9\n    │\n258 │         observedAt = block.timestamp;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedTick` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:261:52\n    │\n261 │         if (poolManager.getLiquidity(poolId) != 0) (, observedTick,,) = poolManager.getSlot0(poolId);\n    │                                                    ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/SIMDTESTHook.sol:64:40\n   │\n64 │     constructor(IPoolManager manager_, address token_) {\n   │                                        ━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:130:19\n    │\n130 │                 ? uint256(-params.amountSpecified)\n    │                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:131:19\n    │\n131 │                 : uint256(params.amountSpecified);\n    │                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:133:25\n    │\n133 │         if (magnitude > uint256(type(int256).max) - magnitude / 100) revert UnrepresentableFee();\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SIMDTESTHook.sol:155:13\n    │\n155 │             emit FeeAccrued(Currency.unwrap(currency), amount);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:149:26\n    │\n149 │         uint256 amount = uint256(filled < 0 ? -filled : filled) / 100;\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:151:15\n    │\n151 │         fee = int128(int256(amount));\n    │               ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:151:22\n    │\n151 │         fee = int128(int256(amount));\n    │                      ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SIMDTESTHook.sol:177:13\n    │\n177 │         if (elapsed != 0) {\n    │             ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SIMDTESTHook.sol:180:28\n    │\n180 │             if (sum < 0 && sum % int256(elapsed) != 0) --mean;\n    │                            ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:178:66\n    │\n178 │             int256 sum = cumulativeTick + int256(observedTick) * int256(block.timestamp - observedAt);\n    │                                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:179:26\n    │\n179 │             mean = sum / int256(elapsed);\n    │                          ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:180:34\n    │\n180 │             if (sum < 0 && sum % int256(elapsed) != 0) --mean;\n    │                                  ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:182:44\n    │\n182 │         return TickMath.getSqrtPriceAtTick(int24(mean));\n    │                                            ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int24' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `operation` is updated\n    ╭▸ src/SIMDTESTHook.sol:190:26\n    │\n190 │         if (claims != 0) poolManager.unlock(abi.encode(claims));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SIMDTESTHook.sol:192:9\n    │\n192 │         emit Swept(amount);\n    │         ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/SIMDTESTHook.sol:190:26\n    │\n190 │         if (claims != 0) poolManager.unlock(abi.encode(claims));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-no-eth]: external call can be reentered before `lastBatch` is updated\n    ╭▸ src/SIMDTESTHook.sol:215:42\n    │\n215 │             (spent, burned) = abi.decode(poolManager.unlock(abi.encode(budget, limitX96)), (uint256, uint256));\n    │                                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SIMDTESTHook.sol:198:13\n    │\n198 │         if (block.timestamp - lastBatch < BATCH_INTERVAL) revert BatchTooSoon();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SIMDTESTHook.sol:226:9\n    │\n226 │         emit BatchExecuted(budget, spent, burned, referenceX96, limitX96);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:212:22\n    │\n212 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:212:30\n    │\n212 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:212:67\n    │\n212 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                                                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:212:75\n    │\n212 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                                                                           ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:239:72\n    │\n239 │             poolManager.swap(poolKey(), SwapParams(pairedIsCurrency0, -int256(budget), limit), \"\");\n    │                                                                        ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:240:25\n    │\n240 │         uint256 spent = uint256(-int256(pairedIsCurrency0 ? delta.amount0() : delta.amount1()));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:241:26\n    │\n241 │         uint256 bought = uint256(int256(pairedIsCurrency0 ? delta.amount1() : delta.amount0()));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/SIMDTESTHook.sol:250:13\n    │\n250 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:257:50\n    │\n257 │         cumulativeTick += int256(observedTick) * int256(block.timestamp - observedAt);\n    │                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:280:16\n    │\n280 │         return uint160(limit);\n    │                ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/BatchRegression.t.sol:32:17\n   │\n32 │         vm.warp(block.timestamp + 1);\n   │         ────────━━━━━━━━━━━━━━━───── `vm.warp` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockTimestamp()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/BatchRegression.t.sol:48:25\n   │\n48 │         uint256 start = block.timestamp;\n   │                         ━━━━━━━━━━━━━━━\n49 │         vm.warp(start + 1);\n   │         ────────────────── `vm.warp` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockTimestamp()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/BatchRegression.t.sol:65:17\n   │\n65 │         vm.warp(block.timestamp + 1);\n   │         ────────━━━━━━━━━━━━━━━───── `vm.warp` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockTimestamp()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/BatchRegression.t.sol:79:25\n   │\n79 │         uint256 start = block.timestamp;\n   │                         ━━━━━━━━━━━━━━━\n80 │         vm.warp(start + 3600);\n   │         ───────────────────── `vm.warp` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockTimestamp()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/BatchRegression.t.sol:186:25\n    │\n186 │         uint256 start = block.timestamp;\n    │                         ━━━━━━━━━━━━━━━\n187 │         vm.warp(start + 3600);\n    │         ───────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/BatchRegression.t.sol:212:25\n    │\n212 │         uint256 start = block.timestamp;\n    │                         ━━━━━━━━━━━━━━━\n213 │         vm.warp(start + 3600);\n    │         ───────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/BatchRegression.t.sol:241:17\n    │\n241 │         vm.warp(block.timestamp + 3600);\n    │         ────────━━━━━━━━━━━━━━━──────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/SIMDTESTHook.t.sol:131:17\n    │\n131 │         vm.warp(block.timestamp + 3600);\n    │         ────────━━━━━━━━━━━━━━━──────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/SIMDTESTHook.t.sol:147:17\n    │\n147 │         vm.warp(block.timestamp + 3600);\n    │         ────────━━━━━━━━━━━━━━━──────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/SIMDTESTHook.t.sol:171:33\n    │\n171 │         uint256 initializedAt = block.timestamp;\n    │                                 ━━━━━━━━━━━━━━━\n172 │         vm.warp(initializedAt + 3599);\n    │         ───────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":4057,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/MainnetFork.t.sol:MainnetForkTest\n[SKIP: skipped: Supply a mainnet fork via forge --fork-url to rehearse against deployed contracts] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 927.75µs (0.00ns CPU time)\n\nRan 1 test for test/MainnetFork.t.sol:MainnetForkToken1Test\n[SKIP: skipped: Supply a mainnet fork via forge --fork-url to rehearse against deployed contracts] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 935.50µs (0.00ns CPU time)\n\nRan 2 tests for test/SIMDTESTHook.t.sol:FreshManagerTest\n[PASS] test_TokenOnlyLiquidityExactOutputBuyCollectsIMDFeeBeforeInputArrives() (gas: 770790)\n[PASS] test_ZeroLiquiditySwapAndBatchHaveZeroFeesAndNoDeadlock() (gas: 430868)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 12.93ms (988.87µs CPU time)\n\nRan 5 tests for test/DeploymentBoundaries.t.sol:DeploymentBoundariesTest\n[PASS] test_ConstructorRejectsNonContractsAndIdenticalCurrencies() (gas: 7598)\n[PASS] test_Create2RejectsAddressWithIncorrectPermissionBits() (gas: 23011)\n[PASS] test_UninitializedBatchFailsButSweepAndLaterInitializationWork() (gas: 526348)\n[PASS] test_UnrepresentableFeeBoundaryForBothSpecifiedSigns() (gas: 85153)\n[PASS] test_WrongPoolBeforeFirstInitializationCannotPoisonLaunch() (gas: 580919)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 17.05ms (762.56µs CPU time)\n\nRan 18 tests for test/SIMDTESTHook.t.sol:TokenIsCurrency1Test\n[PASS] testFuzz_FeeMatchesRealFill(bool,bool,uint256) (runs: 1000, μ: 322869, ~: 331285)\n[PASS] testFuzz_PartialFillOnlyTaxesFilledUnspecifiedCurrency(bool,bool,uint24) (runs: 1000, μ: 336183, ~: 333671)\n[PASS] test_AllCallbacksRejectUnauthorizedCallers() (gas: 141735)\n[PASS] test_AnyoneSweepsTokenFeesImmediatelyAndIdempotently() (gas: 677558)\n[PASS] test_BatchPriceLimitAcceptsPartialFillAndRemainderLater() (gas: 555789)\n[PASS] test_BatchUses25PercentBurnsOutputAndChargesNoHookFee() (gas: 706848)\n[PASS] test_CannotBatchInsideSomeoneElsesUnlock() (gas: 78089)\n[PASS] test_CooldownBoundaryAndEmptyBatch() (gas: 523862)\n[PASS] test_ExactInputBuy() (gas: 336054)\n[PASS] test_ExactInputSell() (gas: 329548)\n[PASS] test_ExactOutputBuy() (gas: 335164)\n[PASS] test_ExactOutputSell() (gas: 330094)\n[PASS] test_OutsideLimitSkipsThenRecoversWithoutSpending() (gas: 566353)\n[PASS] test_PermissionBitsAndInitialization() (gas: 41675)\n[PASS] test_RejectsOtherPoolsAndReinitialization() (gas: 126177)\n[PASS] test_RuntimeAndCreationSizeAndNoEscapeOpcodes() (gas: 2743646)\n[PASS] test_TimeWeightedReferenceExcludesSameTimestampManipulation() (gas: 492259)\n[PASS] test_UnrepresentableSpecifiedAmountRefused() (gas: 91061)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 277.28ms (399.87ms CPU time)\n\nRan 16 tests for test/BatchRegression.t.sol:BatchRegressionToken0Test\n[PASS] testFuzz_DriftedBatchRespectsSpotBandAndBudget(uint24,uint16) (runs: 1000, μ: 842960, ~: 842350)\n[PASS] test_CoreTickBoundaryConventionIsPreserved() (gas: 763222)\n[PASS] test_DriftedReferenceCannotAllowMoreThanThreePercentSpotImpact() (gas: 828824)\nLogs:\n  price ratio (1e18): 1029999999999999997\n\n[PASS] test_DustFeesRoundDown() (gas: 26500959)\n[PASS] test_EmptyPoolAttemptsPreserveReferenceAndSlot() (gas: 324657)\n[PASS] test_FractionalMeanTickRoundsDown() (gas: 532034)\n[PASS] test_LargeSandwichAfterDriftLosesMoney() (gas: 1171274)\nLogs:\n  sandwich profit in IMD wei: -6426335962633652393075\n\n[PASS] test_NonatomicDeploymentAllowsFirstInitializer() (gas: 6181688)\n[PASS] test_NonzeroFillThatExhaustsLiquidityDoesNotRecordVoidTick() (gas: 1681455)\n[PASS] test_QuietPoolReanchorsAtEachFilledBatch() (gas: 743262)\n[PASS] test_RepeatedOutOfBandAttemptsPreserveEpochAndHourlySlot() (gas: 838502)\n[PASS] test_SandwichWithoutDriftControlLosesMoney() (gas: 978477)\nLogs:\n  sandwich profit in IMD wei: -1284636568137345946979\n\n[PASS] test_SmallSandwichAfterDriftLosesMoney() (gas: 1171359)\nLogs:\n  sandwich profit in IMD wei: -713637653871767692221\n\n[PASS] test_VoidExcursionThenBuyerRestoresSpot() (gas: 1517985)\n[PASS] test_ZeroFillGriefCannotConsumeSlotOrResetTwap() (gas: 1317008)\n[PASS] test_ZeroFillVoidDoesNotPoisonReferenceOrBlockBatch() (gas: 1382414)\nSuite result: ok. 16 passed; 0 failed; 0 skipped; finished in 277.34ms (318.03ms CPU time)\n\nRan 18 tests for test/SIMDTESTHook.t.sol:TokenIsCurrency0Test\n[PASS] testFuzz_FeeMatchesRealFill(bool,bool,uint256) (runs: 1000, μ: 324631, ~: 331287)\n[PASS] testFuzz_PartialFillOnlyTaxesFilledUnspecifiedCurrency(bool,bool,uint24) (runs: 1000, μ: 336633, ~: 338638)\n[PASS] test_AllCallbacksRejectUnauthorizedCallers() (gas: 141735)\n[PASS] test_AnyoneSweepsTokenFeesImmediatelyAndIdempotently() (gas: 681057)\n[PASS] test_BatchPriceLimitAcceptsPartialFillAndRemainderLater() (gas: 522021)\n[PASS] test_BatchUses25PercentBurnsOutputAndChargesNoHookFee() (gas: 712631)\n[PASS] test_CannotBatchInsideSomeoneElsesUnlock() (gas: 78089)\n[PASS] test_CooldownBoundaryAndEmptyBatch() (gas: 496816)\n[PASS] test_ExactInputBuy() (gas: 329544)\n[PASS] test_ExactInputSell() (gas: 336058)\n[PASS] test_ExactOutputBuy() (gas: 330093)\n[PASS] test_ExactOutputSell() (gas: 335165)\n[PASS] test_OutsideLimitSkipsThenRecoversWithoutSpending() (gas: 556380)\n[PASS] test_PermissionBitsAndInitialization() (gas: 41675)\n[PASS] test_RejectsOtherPoolsAndReinitialization() (gas: 126177)\n[PASS] test_RuntimeAndCreationSizeAndNoEscapeOpcodes() (gas: 2743646)\n[PASS] test_TimeWeightedReferenceExcludesSameTimestampManipulation() (gas: 486281)\n[PASS] test_UnrepresentableSpecifiedAmountRefused() (gas: 91079)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 279.46ms (525.21ms CPU time)\n\nRan 10 tests for test/AdversarialHook.t.sol:AdversarialToken1Test\n[PASS] testFuzz_FailedSettlementRollsBackClaimsAndOracle(bool,uint256) (runs: 1000, μ: 995615, ~: 985792)\n[PASS] testFuzz_HugeRequestsStillChargeOnlyActualPartialFill(bool,bool,uint256) (runs: 1000, μ: 336230, ~: 333788)\n[PASS] test_BackedClaimDonationsAreIncludedAndCannotBeStolen() (gas: 1424017)\n[PASS] test_DustSwapsAroundOnePercentRoundingBoundary() (gas: 6439439)\n[PASS] test_FailedBatchRestoresBudgetOracleAndCooldownForRetry() (gas: 799189)\n[PASS] test_FailedSweepRestoresClaimsAndAllowsImmediateRetry() (gas: 573623)\n[PASS] test_MixedClaimsAndDirectIMDSettleOnlyWhatBatchSpends() (gas: 773547)\n[PASS] test_NestedSweepAndBatchCannotRedeemDuringAnotherUnlock() (gas: 1454006)\n[PASS] test_PartialBatchPreservesUnspentClaimsForNextHour() (gas: 1458133)\n[PASS] test_SubFourWeiBudgetStaysAccruedAndDoesNotBlockSweeping() (gas: 787477)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 279.51ms (407.44ms CPU time)\n\nRan 16 tests for test/BatchRegression.t.sol:BatchRegressionToken1Test\n[PASS] testFuzz_DriftedBatchRespectsSpotBandAndBudget(uint24,uint16) (runs: 1000, μ: 838130, ~: 836362)\n[PASS] test_CoreTickBoundaryConventionIsPreserved() (gas: 763209)\n[PASS] test_DriftedReferenceCannotAllowMoreThanThreePercentSpotImpact() (gas: 822764)\nLogs:\n  price ratio (1e18): 970000000000000001\n\n[PASS] test_DustFeesRoundDown() (gas: 26394679)\n[PASS] test_EmptyPoolAttemptsPreserveReferenceAndSlot() (gas: 330909)\n[PASS] test_FractionalMeanTickRoundsDown() (gas: 532021)\n[PASS] test_LargeSandwichAfterDriftLosesMoney() (gas: 1164045)\nLogs:\n  sandwich profit in IMD wei: -6060400525509601185178\n\n[PASS] test_NonatomicDeploymentAllowsFirstInitializer() (gas: 36406548)\n[PASS] test_NonzeroFillThatExhaustsLiquidityDoesNotRecordVoidTick() (gas: 1692805)\n[PASS] test_QuietPoolReanchorsAtEachFilledBatch() (gas: 776907)\n[PASS] test_RepeatedOutOfBandAttemptsPreserveEpochAndHourlySlot() (gas: 845466)\n[PASS] test_SandwichWithoutDriftControlLosesMoney() (gas: 984601)\nLogs:\n  sandwich profit in IMD wei: -1240180979413197579140\n\n[PASS] test_SmallSandwichAfterDriftLosesMoney() (gas: 1163321)\nLogs:\n  sandwich profit in IMD wei: -673009625310132996892\n\n[PASS] test_VoidExcursionThenBuyerRestoresSpot() (gas: 1521145)\n[PASS] test_ZeroFillGriefCannotConsumeSlotOrResetTwap() (gas: 1336427)\n[PASS] test_ZeroFillVoidDoesNotPoisonReferenceOrBlockBatch() (gas: 1384994)\nSuite result: ok. 16 passed; 0 failed; 0 skipped; finished in 279.59ms (324.36ms CPU time)\n\nRan 10 tests for test/AdversarialHook.t.sol:AdversarialToken0Test\n[PASS] testFuzz_FailedSettlementRollsBackClaimsAndOracle(bool,uint256) (runs: 1000, μ: 970556, ~: 971377)\n[PASS] testFuzz_HugeRequestsStillChargeOnlyActualPartialFill(bool,bool,uint256) (runs: 1000, μ: 336675, ~: 338729)\n[PASS] test_BackedClaimDonationsAreIncludedAndCannotBeStolen() (gas: 1397303)\n[PASS] test_DustSwapsAroundOnePercentRoundingBoundary() (gas: 6425324)\n[PASS] test_FailedBatchRestoresBudgetOracleAndCooldownForRetry() (gas: 804843)\n[PASS] test_FailedSweepRestoresClaimsAndAllowsImmediateRetry() (gas: 567155)\n[PASS] test_MixedClaimsAndDirectIMDSettleOnlyWhatBatchSpends() (gas: 767559)\n[PASS] test_NestedSweepAndBatchCannotRedeemDuringAnotherUnlock() (gas: 1430159)\n[PASS] test_PartialBatchPreservesUnspentClaimsForNextHour() (gas: 1423823)\n[PASS] test_SubFourWeiBudgetStaysAccruedAndDoesNotBlockSweeping() (gas: 760431)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 279.58ms (556.24ms CPU time)\n\nRan 3 tests for test/SIMDTEST.t.sol:SIMDTESTTest\n[PASS] testFuzz_TransferAndAllowanceConserveSupply(uint256) (runs: 1000, μ: 169915, ~: 170167)\n[PASS] test_FactoryReceivesEntirePolicySupply() (gas: 44103)\n[PASS] test_NoMintOwnerUpgradeOrPauseEntrypoints() (gas: 145216)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 279.58ms (279.53ms CPU time)\n\nRan 2 tests for test/Conservation.invariant.t.sol:ConservationInvariantTest\n[PASS] invariant_AllFeeAssetsAreAccountedFor() (runs: 256, calls: 16384, reverts: 0)\n\n╭---------------+--------------+-------+---------+----------╮\n| Contract      | Selector     | Calls | Reverts | Discards |\n+===========================================================+\n| LaunchHandler | batch        | 2729  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | donate       | 2815  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | elapse       | 2703  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | limitedTrade | 2704  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | sweep        | 2681  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | trade        | 2752  | 0       | 0        |\n╰---------------+--------------+-------+---------+----------╯\n\n[PASS] test_HandlerEmptyAndDustAttemptsRetainHistoryUntilFundedRetry() (gas: 2053905)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 3.94s (3.93s CPU time)\n\nRan 2 tests for test/Conservation.invariant.t.sol:ConservationToken1InvariantTest\n[PASS] invariant_AllFeeAssetsAreAccountedFor() (runs: 256, calls: 16384, reverts: 0)\n\n╭---------------+--------------+-------+---------+----------╮\n| Contract      | Selector     | Calls | Reverts | Discards |\n+===========================================================+\n| LaunchHandler | batch        | 2729  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | donate       | 2815  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | elapse       | 2703  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | limitedTrade | 2704  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | sweep        | 2681  | 0       | 0        |\n|---------------+--------------+-------+---------+----------|\n| LaunchHandler | trade        | 2752  | 0       | 0        |\n╰---------------+--------------+-------+---------+----------╯\n\n[PASS] test_HandlerEmptyAndDustAttemptsRetainHistoryUntilFundedRetry() (gas: 2064671)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 3.97s (3.95s CPU time)\n\nRan 13 test suites in 3.97s (9.89s CPU time): 102 tests passed, 0 failed, 2 skipped (104 total tests)\n","passed":true},{"durationMs":53,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"SIMDTEST.approve(address,uint256)\",\"SIMDTEST.transfer(address,uint256)\",\"SIMDTEST.transferFrom(address,address,uint256)\",\"SIMDTESTHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"SIMDTESTHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"SIMDTESTHook.executeBatch()\",\"SIMDTESTHook.sweep()\",\"SIMDTESTHook.unlockCallback(bytes)\"],\"files\":{\".gitignore\":4,\"LICENSE\":23,\"README.md\":237,\"dependencies.json\":19,\"foundry.toml\":20,\"launch.json\":16,\"remappings.txt\":4,\"script/MineHook.s.sol\":28,\"src/HookFlags.sol\":29,\"src/SIMDTEST.sol\":11,\"src/SIMDTESTHook.sol\":282,\"test/AdversarialHook.t.sol\":299,\"test/BatchRegression.t.sol\":297,\"test/Conservation.invariant.t.sol\":269,\"test/DeploymentBoundaries.t.sol\":117,\"test/MainnetFork.t.sol\":32,\"test/README.md\":36,\"test/SIMDTEST.t.sol\":53,\"test/SIMDTESTHook.t.sol\":285,\"test/helpers/ClaimActions.sol\":49,\"test/helpers/LaunchFixture.sol\":133,\"test/helpers/PoolActor.sol\":81,\"test/helpers/SwapEvidence.sol\":40,\"test/mocks/MockERC20.sol\":15},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"a5b17f40dee1a4337d9e1c097cf5a11b3829803a363cc05546cab6c449e02d09","verifiedTreeHash":"2c785acf36ff97d6b951f18f1c7dbf2774dd63d0","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":4360,"exitCode":0,"name":"build","output":"Compiling 82 files with Solc 0.8.26\nSolc 0.8.26 finished in 4.08s\nCompiler run successful!\nwarning[missing-events-arithmetic]: `observedTick` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:113:9\n    │\n113 │         observedTick = TickMath.getTickAtSqrtPrice(sqrtPriceX96);\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedAt` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:114:9\n    │\n114 │         observedAt = block.timestamp;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `epochStart` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:115:9\n    │\n115 │         epochStart = block.timestamp;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `lastBatch` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:116:9\n    │\n116 │         lastBatch = block.timestamp;\n    │         ━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `cumulativeTick` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:246:9\n    │\n246 │         cumulativeTick += int256(observedTick) * int256(block.timestamp - observedAt);\n    │         ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedAt` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:247:9\n    │\n247 │         observedAt = block.timestamp;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedTick` is changed without an event but is used in arithmetic\n    ╭▸ src/SIMDTESTHook.sol:248:9\n    │\n248 │         (, observedTick,,) = poolManager.getSlot0(poolId);\n    │         ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/SIMDTESTHook.sol:64:40\n   │\n64 │     constructor(IPoolManager manager_, address token_) {\n   │                                        ━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:130:19\n    │\n130 │                 ? uint256(-params.amountSpecified)\n    │                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:131:19\n    │\n131 │                 : uint256(params.amountSpecified);\n    │                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:133:25\n    │\n133 │         if (magnitude > uint256(type(int256).max) - magnitude / 100) revert UnrepresentableFee();\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SIMDTESTHook.sol:155:13\n    │\n155 │             emit FeeAccrued(Currency.unwrap(currency), amount);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:149:26\n    │\n149 │         uint256 amount = uint256(filled < 0 ? -filled : filled) / 100;\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:151:15\n    │\n151 │         fee = int128(int256(amount));\n    │               ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:151:22\n    │\n151 │         fee = int128(int256(amount));\n    │                      ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SIMDTESTHook.sol:177:13\n    │\n177 │         if (elapsed != 0) {\n    │             ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SIMDTESTHook.sol:180:28\n    │\n180 │             if (sum < 0 && sum % int256(elapsed) != 0) --mean;\n    │                            ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:178:66\n    │\n178 │             int256 sum = cumulativeTick + int256(observedTick) * int256(block.timestamp - observedAt);\n    │                                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:179:26\n    │\n179 │             mean = sum / int256(elapsed);\n    │                          ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:180:34\n    │\n180 │             if (sum < 0 && sum % int256(elapsed) != 0) --mean;\n    │                                  ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:182:44\n    │\n182 │         return TickMath.getSqrtPriceAtTick(int24(mean));\n    │                                            ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int24' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `operation` is updated\n    ╭▸ src/SIMDTESTHook.sol:190:26\n    │\n190 │         if (claims != 0) poolManager.unlock(abi.encode(claims));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SIMDTESTHook.sol:192:9\n    │\n192 │         emit Swept(amount);\n    │         ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/SIMDTESTHook.sol:190:26\n    │\n190 │         if (claims != 0) poolManager.unlock(abi.encode(claims));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-no-eth]: external call can be reentered before `observedTick` is updated\n    ╭▸ src/SIMDTESTHook.sol:208:42\n    │\n208 │             (spent, burned) = abi.decode(poolManager.unlock(abi.encode(budget, limitX96)), (uint256, uint256));\n    │                                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SIMDTESTHook.sol:198:13\n    │\n198 │         if (block.timestamp - lastBatch < BATCH_INTERVAL) revert BatchTooSoon();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SIMDTESTHook.sol:215:9\n    │\n215 │         emit BatchExecuted(budget, spent, burned, referenceX96, limitX96);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:203:22\n    │\n203 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:203:30\n    │\n203 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:203:67\n    │\n203 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                                                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:203:75\n    │\n203 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                                                                           ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:228:72\n    │\n228 │             poolManager.swap(poolKey(), SwapParams(pairedIsCurrency0, -int256(budget), limit), \"\");\n    │                                                                        ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:229:25\n    │\n229 │         uint256 spent = uint256(-int256(pairedIsCurrency0 ? delta.amount0() : delta.amount1()));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:230:26\n    │\n230 │         uint256 bought = uint256(int256(pairedIsCurrency0 ? delta.amount1() : delta.amount0()));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/SIMDTESTHook.sol:239:13\n    │\n239 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:246:50\n    │\n246 │         cumulativeTick += int256(observedTick) * int256(block.timestamp - observedAt);\n    │                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SIMDTESTHook.sol:267:16\n    │\n267 │         return uint160(limit);\n    │                ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/SIMDTESTHook.t.sol:131:17\n    │\n131 │         vm.warp(block.timestamp + 3600);\n    │         ────────━━━━━━━━━━━━━━━──────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/SIMDTESTHook.t.sol:147:17\n    │\n147 │         vm.warp(block.timestamp + 3600);\n    │         ────────━━━━━━━━━━━━━━━──────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/SIMDTESTHook.t.sol:171:33\n    │\n171 │         uint256 initializedAt = block.timestamp;\n    │                                 ━━━━━━━━━━━━━━━\n172 │         vm.warp(initializedAt + 3599);\n    │         ───────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":1441,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/MainnetFork.t.sol:MainnetForkTest\n[SKIP: skipped: Supply a mainnet fork via forge --fork-url to rehearse against deployed contracts] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 831.60µs (0.00ns CPU time)\n\nRan 3 tests for test/SIMDTEST.t.sol:SIMDTESTTest\n[PASS] testFuzz_TransferAndAllowanceConserveSupply(uint256) (runs: 1000, μ: 169849, ~: 170167)\n[PASS] test_FactoryReceivesEntirePolicySupply() (gas: 44103)\n[PASS] test_NoMintOwnerUpgradeOrPauseEntrypoints() (gas: 145216)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 19.57ms (19.55ms CPU time)\n\nRan 2 tests for test/SIMDTESTHook.t.sol:FreshManagerTest\n[PASS] test_TokenOnlyLiquidityExactOutputBuyCollectsIMDFeeBeforeInputArrives() (gas: 766741)\n[PASS] test_ZeroLiquiditySwapAndBatchHaveZeroFeesAndNoDeadlock() (gas: 461647)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 70.49ms (1.23ms CPU time)\n\nRan 18 tests for test/SIMDTESTHook.t.sol:TokenIsCurrency1Test\n[PASS] testFuzz_FeeMatchesRealFill(bool,bool,uint256) (runs: 1000, μ: 321543, ~: 330097)\n[PASS] testFuzz_PartialFillOnlyTaxesFilledUnspecifiedCurrency(bool,bool,uint24) (runs: 1000, μ: 335233, ~: 337356)\n[PASS] test_AllCallbacksRejectUnauthorizedCallers() (gas: 141735)\n[PASS] test_AnyoneSweepsTokenFeesImmediatelyAndIdempotently() (gas: 675180)\n[PASS] test_BatchPriceLimitAcceptsPartialFillAndRemainderLater() (gas: 554151)\n[PASS] test_BatchUses25PercentBurnsOutputAndChargesNoHookFee() (gas: 701522)\n[PASS] test_CannotBatchInsideSomeoneElsesUnlock() (gas: 78089)\n[PASS] test_CooldownBoundaryAndEmptyBatch() (gas: 193382)\n[PASS] test_ExactInputBuy() (gas: 334865)\n[PASS] test_ExactInputSell() (gas: 328359)\n[PASS] test_ExactOutputBuy() (gas: 333975)\n[PASS] test_ExactOutputSell() (gas: 328905)\n[PASS] test_OutsideLimitSkipsThenRecoversWithoutSpending() (gas: 565191)\n[PASS] test_PermissionBitsAndInitialization() (gas: 41675)\n[PASS] test_RejectsOtherPoolsAndReinitialization() (gas: 126177)\n[PASS] test_RuntimeAndCreationSizeAndNoEscapeOpcodes() (gas: 2652632)\n[PASS] test_TimeWeightedReferenceExcludesSameTimestampManipulation() (gas: 489881)\n[PASS] test_UnrepresentableSpecifiedAmountRefused() (gas: 91061)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 118.25ms (193.91ms CPU time)\n\nRan 18 tests for test/SIMDTESTHook.t.sol:TokenIsCurrency0Test\n[PASS] testFuzz_FeeMatchesRealFill(bool,bool,uint256) (runs: 1000, μ: 321412, ~: 330047)\n[PASS] testFuzz_PartialFillOnlyTaxesFilledUnspecifiedCurrency(bool,bool,uint24) (runs: 1000, μ: 335192, ~: 332457)\n[PASS] test_AllCallbacksRejectUnauthorizedCallers() (gas: 141711)\n[PASS] test_AnyoneSweepsTokenFeesImmediatelyAndIdempotently() (gas: 678643)\n[PASS] test_BatchPriceLimitAcceptsPartialFillAndRemainderLater() (gas: 516337)\n[PASS] test_BatchUses25PercentBurnsOutputAndChargesNoHookFee() (gas: 707393)\n[PASS] test_CannotBatchInsideSomeoneElsesUnlock() (gas: 78077)\n[PASS] test_CooldownBoundaryAndEmptyBatch() (gas: 193299)\n[PASS] test_ExactInputBuy() (gas: 328343)\n[PASS] test_ExactInputSell() (gas: 334857)\n[PASS] test_ExactOutputBuy() (gas: 328892)\n[PASS] test_ExactOutputSell() (gas: 333964)\n[PASS] test_OutsideLimitSkipsThenRecoversWithoutSpending() (gas: 555381)\n[PASS] test_PermissionBitsAndInitialization() (gas: 41675)\n[PASS] test_RejectsOtherPoolsAndReinitialization() (gas: 126141)\n[PASS] test_RuntimeAndCreationSizeAndNoEscapeOpcodes() (gas: 2652632)\n[PASS] test_TimeWeightedReferenceExcludesSameTimestampManipulation() (gas: 483879)\n[PASS] test_UnrepresentableSpecifiedAmountRefused() (gas: 91067)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 119.03ms (122.91ms CPU time)\n\nRan 1 test for test/Conservation.invariant.t.sol:ConservationInvariantTest\n[PASS] invariant_AllFeeAssetsAreAccountedFor() (runs: 256, calls: 8192, reverts: 0)\n\n╭---------------+----------+-------+---------+----------╮\n| Contract      | Selector | Calls | Reverts | Discards |\n+=======================================================+\n| LaunchHandler | batch    | 2091  | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| LaunchHandler | elapse   | 2022  | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| LaunchHandler | sweep    | 2015  | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| LaunchHandler | trade    | 2064  | 0       | 0        |\n╰---------------+----------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.33s (1.25s CPU time)\n\nRan 6 test suites in 1.33s (1.66s CPU time): 42 tests passed, 0 failed, 1 skipped (43 total tests)\n","passed":true},{"durationMs":74,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"SIMDTEST.approve(address,uint256)\",\"SIMDTEST.transfer(address,uint256)\",\"SIMDTEST.transferFrom(address,address,uint256)\",\"SIMDTESTHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"SIMDTESTHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"SIMDTESTHook.executeBatch()\",\"SIMDTESTHook.sweep()\",\"SIMDTESTHook.unlockCallback(bytes)\"],\"files\":{\".gitignore\":4,\"LICENSE\":23,\"README.md\":209,\"dependencies.json\":19,\"foundry.toml\":20,\"launch.json\":16,\"remappings.txt\":4,\"script/MineHook.s.sol\":28,\"src/HookFlags.sol\":29,\"src/SIMDTEST.sol\":11,\"src/SIMDTESTHook.sol\":269,\"test/Conservation.invariant.t.sol\":90,\"test/MainnetFork.t.sol\":17,\"test/SIMDTEST.t.sol\":53,\"test/SIMDTESTHook.t.sol\":278,\"test/helpers/LaunchFixture.sol\":129,\"test/helpers/PoolActor.sol\":81,\"test/mocks/MockERC20.sol\":15},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":1727,"exitCode":0,"name":"slither","output":"[high/medium] weak-prng at src/SIMDTESTHook.sol:173: SIMDTESTHook.referencePrice() (src/SIMDTESTHook.sol#173-183) uses a weak PRNG: \"sum < 0 && sum % int256(elapsed) != 0 (src/SIMDTESTHook.sol#180)\"\n[medium/medium] reentrancy-no-eth at src/SIMDTESTHook.sol:196: Reentrancy in SIMDTESTHook.executeBatch() (src/SIMDTESTHook.sol#196-216):\n[medium/medium] unused-return at src/SIMDTESTHook.sol:218: SIMDTESTHook.unlockCallback(bytes) (src/SIMDTESTHook.sol#218-243) ignores return value by poolManager.settle() (src/SIMDTESTHook.sol#239)\n[medium/medium] unused-return at src/SIMDTESTHook.sol:196: SIMDTESTHook.executeBatch() (src/SIMDTESTHook.sol#196-216) ignores return value by (None,observedTick,None,None) = poolManager.getSlot0(poolId) (src/SIMDTESTHook.sol#211)\n[medium/medium] unused-return at src/SIMDTESTHook.sol:186: SIMDTESTHook.sweep() (src/SIMDTESTHook.sol#186-193) ignores return value by poolManager.unlock(abi.encode(claims)) (src/SIMDTESTHook.sol#190)\n[medium/medium] unused-return at src/SIMDTESTHook.sol:245: SIMDTESTHook._observe() (src/SIMDTESTHook.sol#245-249) ignores return value by (None,observedTick,None,None) = poolManager.getSlot0(poolId) (src/SIMDTESTHook.sol#248)\n[medium/medium] unused-return at src/SIMDTESTHook.sol:196: SIMDTESTHook.executeBatch() (src/SIMDTESTHook.sol#196-216) ignores return value by (spot,None,None,None) = poolManager.getSlot0(poolId) (src/SIMDTESTHook.sol#205)\n[low/medium] events-maths at src/SIMDTESTHook.sol:104: SIMDTESTHook.beforeInitialize(address,PoolKey,uint160) (src/SIMDTESTHook.sol#104-118) should emit an event for: \n[low/medium] reentrancy-events at src/SIMDTESTHook.sol:137: Reentrancy in SIMDTESTHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/SIMDTESTHook.sol#137-158):\n[low/medium] reentrancy-events at src/SIMDTESTHook.sol:186: Reentrancy in SIMDTESTHook.sweep() (src/SIMDTESTHook.sol#186-193):\n[low/medium] reentrancy-events at src/SIMDTESTHook.sol:196: Reentrancy in SIMDTESTHook.executeBatch() (src/SIMDTESTHook.sol#196-216):\n[low/medium] timestamp at src/SIMDTESTHook.sol:196: SIMDTESTHook.executeBatch() (src/SIMDTESTHook.sol#196-216) uses timestamp for comparisons\n[low/medium] timestamp at src/SIMDTESTHook.sol:173: SIMDTESTHook.referencePrice() (src/SIMDTESTHook.sol#173-183) uses timestamp for comparisons\n[low/medium] timestamp at src/SIMDTESTHook.sol:259: SIMDTESTHook._priceLimit(uint160) (src/SIMDTESTHook.sol#259-268) uses timestamp for comparisons","passed":true},{"durationMs":476,"exitCode":0,"name":"aderyn","output":"[high] reentrancy-state-change at src/SIMDTESTHook.sol:208: Reentrancy: State change after external call\n[high] unsafe-casting at src/SIMDTESTHook.sol:182: Unsafe Casting of integers\n[low] internal-function-used-once at src/HookFlags.sol:22: Internal Function Used Only Once\n[low] large-numeric-literal at src/SIMDTEST.sol:9: Large Numeric Literal\n[low] literal-instead-of-constant at src/SIMDTESTHook.sol:133: Literal Instead of Constant (4 places)\n[low] state-change-without-event at src/SIMDTESTHook.sol:104: State Change Without Event\n[low] unchecked-return at src/SIMDTESTHook.sol:239: Unchecked Return\n[low] unsafe-erc20-operation at src/SIMDTESTHook.sol:191: Unsafe ERC20 Operation (2 places)\n[low] unused-public-function at src/SIMDTESTHook.sol:166: Public Function Not Used Internally","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"de34b0cffb20bc3a2e03c4bd2cbcfae1754a615af284b558cf7b65fa5436be88","verifiedTreeHash":"44f6f655eeea24295918b99d26eb76c19ea64a46","verifierVersion":"0.1.0+ad90ce4c"}]}